home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gcc / ixemulsrc.lha / ixemul / library / vfork.c < prev    next >
C/C++ Source or Header  |  1996-12-11  |  23KB  |  767 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *  Portions Copyright (C) 1994 Rafael W. Luebbert
  5.  *
  6.  *  This library is free software; you can redistribute it and/or
  7.  *  modify it under the terms of the GNU Library General Public
  8.  *  License as published by the Free Software Foundation; either
  9.  *  version 2 of the License, or (at your option) any later version.
  10.  *
  11.  *  This library is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  *  Library General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU Library General Public
  17.  *  License along with this library; if not, write to the Free
  18.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  *
  20.  *  $Id: vfork.c,v 1.9 1994/06/19 15:18:29 rluebbert Exp $
  21.  *
  22.  *  $Log: vfork.c,v $
  23.  *  Revision 1.9  1994/06/19  15:18:29  rluebbert
  24.  *  *** empty log message ***
  25.  *
  26.  *  Revision 1.7  1992/10/20  16:29:49  mwild
  27.  *  allow a vfork'd process to use the parents memory pool. The new function
  28.  *  vfork2() continues to use the old semantics.
  29.  *
  30.  *  Revision 1.6  1992/09/14  01:48:11  mwild
  31.  *  move kmalloc() out of Forbid() (since the allocator is now Semaphore-based).
  32.  *  move errno assignment after sigsetmask (thanks Niklas!)
  33.  *  remove dead code
  34.  *
  35.  *  Revision 1.5  1992/08/09  21:01:43  amiga
  36.  *  change to 2.x header files
  37.  *  duplicate calling stack frame in vfork_resume() instead of just doing rts.
  38.  *  temporary abort calling 1.3 vfork, until that's fixed again (when???).
  39.  *
  40.  *  Revision 1.4  1992/07/04  19:24:12  mwild
  41.  *  get passing of environment right.
  42.  *  change ix_sleep() calls to new semantics.
  43.  *
  44.  * Revision 1.3  1992/05/18  12:26:25  mwild
  45.  * fixed bad typo that didn't close files before sending wait message.
  46.  * Set childs Input()/Output() to NIL:, we only keep the files in our
  47.  * own filetable.
  48.  *
  49.  * Revision 1.2  1992/05/18  01:02:31  mwild
  50.  * add temporary Delay(100) before CloseLibrary() in the child after
  51.  * vfork(), there seem to arrive some late packets (don't know why..)
  52.  * pass NIL: filehandles as Input()/Output() to the child, so that the
  53.  * real I/O-handles only depend on ix-filetable for usage-count
  54.  *
  55.  * Revision 1.1  1992/05/14  19:55:40  mwild
  56.  * Initial revision
  57.  *
  58.  */
  59.  
  60. #define _KERNEL
  61. #include "ixemul.h"
  62. #include "kprintf.h"
  63.  
  64. #include <sys/syscall.h>
  65. #include <sys/resource.h>
  66. #include <sys/wait.h>
  67. #include <stddef.h>
  68. #include <setjmp.h>
  69. #include <string.h>
  70. #include <stdlib.h>
  71.  
  72. #include <utility/tagitem.h>
  73. #include <dos/dostags.h>
  74.  
  75. #include "version.h"
  76.  
  77. void vfork_own_malloc ();
  78. void volatile vfork_longjmp (jmp_buf, int);
  79. void ruadd(struct rusage *ru, struct rusage *ru2);
  80.  
  81. /* having it in a struct makes parameter passing easier */
  82.  
  83. struct reg_parms {
  84.   jmp_buf jb;
  85. };
  86.  
  87. struct vfork_msg {
  88.   struct Message     vm_msg;
  89.   struct Process    *vm_self;    /* for validation purposes */
  90.   struct Process     *vm_pptr;
  91.   struct reg_parms     *vm_regs;    /* parents context to restore */
  92.   int            vm_own_malloc;
  93.   int             vm_rc;        /* 0 if the child started normally, else errno */
  94. };
  95.  
  96. struct death_msg {
  97.   struct MinNode    dm_node;
  98.   struct Process    *dm_child;
  99.   int            dm_pgrp;
  100.   int            dm_status;
  101.   struct rusage        dm_rusage;
  102. };
  103.  
  104. /* this is the new process generated by vfork () ! */
  105. static void
  106. launcher (void)
  107. {
  108.   void *ixb = OpenLibrary ("ixemul.library", IX_VERSION);
  109.   struct Process *me = (struct Process *) FindTask (0);
  110.   struct vfork_msg *vm = NULL;
  111.   int omask;
  112.  
  113.   do
  114.     {
  115.       if (!vm) 
  116.         WaitPort (& me->pr_MsgPort);
  117.       vm = (struct vfork_msg *) GetMsg (&me->pr_MsgPort);
  118.     }
  119.   while (!vm || vm->vm_self != me);
  120.       
  121.  
  122.   if (ixb)
  123.     {
  124.       /* get parents user area */
  125.       volatile struct user *pu = getuser(vm->vm_pptr);
  126.       /* `my' user area. This way we don't have to recalculate it too often */
  127.       volatile struct user *mu = &u;
  128.       /* reaping the dup function of execve() ;-)) */
  129.       int fd;
  130.       int a4_size = pu->u_a4_pointers_size * 4;
  131.  
  132.       /* link ourselves into the parents process lists. Guarantee single
  133.        * threaded access to those lists by locking out any other users of
  134.        * the library (nicer than to just call Forbid()) */
  135.       ix_lock_base ();
  136.       
  137.       /* our older sybling is the last recently created child of the parent */
  138.       mu->p_osptr = pu->p_cptr;
  139.       /* we have no younger sybling */
  140.       mu->p_ysptr = 0;
  141.       /* if we have an older sybling, point its `younger sybling' field at us */
  142.       if (mu->p_osptr)
  143.         {
  144.           struct user *ou = getuser(mu->p_osptr);
  145.       ou->p_ysptr = me;
  146.     }
  147.       /* set the parents `last recently created child' field at us */
  148.       pu->p_cptr = me;
  149.  
  150.       /* inherit the session of our parent */
  151.       mu->u_session = pu->u_session;
  152.       if (mu->u_session)
  153.         mu->u_session->s_count++; /* and increase use count */
  154.  
  155.       /* inherit the process group of our parent */
  156.       mu->p_pgrp = pu->p_pgrp;
  157.       mu->p_pptr = vm->vm_pptr;
  158.  
  159.       /* inherit the uid/gid information from parent */
  160.       mu->u_ruid = pu->u_ruid;
  161.       mu->u_euid = pu->u_euid;
  162.       mu->u_rgid = pu->u_rgid;
  163.       mu->u_egid = pu->u_egid;
  164.  
  165.       if ((mu->u_ngroups = pu->u_ngroups)) 
  166.         bcopy((char *)pu->u_grouplist, (char *)mu->u_grouplist, pu->u_ngroups * sizeof(int));
  167.  
  168.       if ((mu->u_logname_valid = pu->u_logname_valid))
  169.         strcpy((char *)mu->u_logname, (char *)pu->u_logname);
  170.             
  171.       /* if we got our own malloc list already, it is safe to call malloc here.
  172.          If not, the stuff done here is postponed to either vfork_resume, or
  173.          execve */
  174.       if (vm->vm_own_malloc)
  175.     {
  176.       /* inherit these global variables. */
  177.       mu->u_environ = (char ***) malloc (4);
  178.       * mu->u_environ = dupvec (* pu->u_environ);
  179.       mu->u_errno = (int *) malloc (4);
  180.       *mu->u_errno = 0;
  181.       mu->u_h_errno = (int *) malloc (4);
  182.       *mu->u_h_errno = 0;
  183.     }
  184.       else
  185.     {
  186.       /* borrow the variables of the parent */
  187.       mu->u_environ = pu->u_environ;
  188.       mu->u_errno = pu->u_errno;
  189.       
  190.       /* tell malloc to use the parents malloc lists */
  191.       mu->u_mdp = pu->u_mdp;
  192.     }
  193.  
  194.       
  195.       /* and inherit several other things as well, upto not including u_md */
  196.       bcopy ((void *)&pu->u_a4_pointers_size, (void *)&mu->u_a4_pointers_size,
  197.          offsetof (struct user, u_md) - offsetof (struct user, u_a4_pointers_size));
  198.       bcopy ((char *)pu - a4_size, (char *)mu - a4_size, a4_size);
  199.  
  200.       /* some things have been copied that should be reset */      
  201.       mu->p_flag &= ~(SFREEA4 | STRC);
  202.       mu->p_xstat = 0;
  203.       bzero ((void *)&mu->u_ru, sizeof (struct rusage));
  204.       bzero ((void *)&mu->u_prof, sizeof (struct uprof));
  205.       mu->u_prof_last_pc = 0;
  206.       bzero ((void *)&mu->u_cru, sizeof (struct rusage));
  207.       bzero ((void *)&mu->u_timer[0], sizeof (struct itimerval)); /* just the REAL timer! */
  208.       syscall (SYS_gettimeofday, & mu->u_start, 0);
  209.       omask = vm->vm_rc;    /* signal mask to restore at the end */
  210.  
  211.       /* and adjust the open count of each of the copied filedescriptors */
  212.       for (fd = 0; fd < NOFILE; fd++)
  213.         if (mu->u_ofile[fd])
  214.           mu->u_ofile[fd]->f_count++;
  215.  
  216.       /* also copy u_segs, after all, the child will run initially in the
  217.      same SegList as the parent. */
  218.       mu->u_segs = pu->u_segs;
  219.       mu->u_start_pc = pu->u_start_pc;
  220.       mu->u_end_pc = pu->u_end_pc;
  221.       
  222.       mu->u_is_root = pu->u_is_root;
  223.       mu->u_a4 = pu->u_a4;
  224.  
  225.       /* copying finished, allow other processes to vfork() as well ;-)) */
  226.       ix_unlock_base ();
  227.       
  228.       /* remember the message we have to reply when either _exit() or 
  229.        * execve() is called */
  230.       mu->p_vfork_msg = vm;
  231.       
  232.       vm->vm_rc = 0;
  233.  
  234.       mu->u_save_sp = (void *) get_sp ();
  235.       /* we get here when the user does an _exit() 
  236.        * (so as well after execve() terminates !) */
  237.       if (_setjmp (mu->u_jmp_buf))
  238.         {
  239.       int i;
  240.  
  241.       /* overkill? */
  242.       vfork_own_malloc ();
  243.  
  244.       /* reset `mu' in here, setjmp() might have clobbered it */
  245.       mu = &u;
  246.  
  247.       /* although this is done in CloseLibrary(), files should 
  248.          really be closed *before* a death-message is sent to
  249.          the parent. */
  250.       for (i = 0; i < NOFILE; i++) 
  251.         if (u.u_ofile[i]) syscall (SYS_close, i);
  252.  
  253.       /* free memory (look that most, best all memory is freed here, as
  254.              long as we're not inside Forbid. If memory is freed in CloseLibrary,
  255.          it may potentially have to wait for the memory semaphore in buddy-alloc.c,
  256.          thus breaking the Forbid! */
  257.       all_free ();
  258.  
  259.       KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  260.  
  261.       /* this whole thing only happens if our parent is still alive ! */
  262.       if (mu->p_pptr && mu->p_pptr != (struct Process *) 1)
  263.         send_death_msg((struct user *)mu);
  264.       else
  265.         {
  266.           Forbid ();
  267.           KPRINTF (("vforked: couldn't send death_msg\n"));
  268.         }
  269.       KPRINTF (("vforked: now closing library\n"));
  270.       CloseLibrary (ixb);
  271.  
  272.       KPRINTF (("vforked: falling off the edge of the world.\n"));
  273.       /* just fall off the edge of the world, this is a process */
  274.       return;
  275.         }
  276.  
  277.       syscall (SYS_sigsetmask, omask);
  278.  
  279.       KPRINTF (("vforked: jumping back\n"));
  280.  
  281.       /* jump into nevereverland ;-) */
  282.       vfork_longjmp (vm->vm_regs->jb, 0);
  283.       /* NOTREACHED */
  284.     }
  285.  
  286.   vm->vm_rc = ENOMEM; /* can't imagine any other reason why the OpenLib should fail */
  287.   ReplyMsg ((struct Message *) vm);
  288.   /* fall off the edge of the world ;-) */
  289. }
  290.  
  291. void
  292. send_death_msg(struct user *mu)
  293. {
  294.   struct death_msg *dm = 0;
  295.  
  296.   /* KPRINTF (("vforked: parent alive, zombie-sig = %ld, vfork_msg = $%lx.\n",
  297.          pu->p_zombie_sig, mu->p_vfork_msg));*/
  298.  
  299.   struct user *pu = getuser(mu->p_pptr);
  300.  
  301.   /* send the parent a death message with our return code */
  302.   dm = (struct death_msg *) kmalloc (sizeof (struct death_msg));
  303.  
  304.   Forbid ();
  305.   if (dm)
  306.     {
  307.       dm->dm_status = mu->p_xstat;
  308.       dm->dm_rusage = mu->u_ru;
  309.       ruadd (&dm->dm_rusage, (struct rusage *)&mu->u_cru);
  310.       dm->dm_child = (struct Process *) FindTask (0);
  311.       dm->dm_pgrp  = mu->p_pgrp;
  312.       KPRINTF (("vfork-exit: Adding child $%lx to $%lx\n", dm->dm_child, mu->p_pptr));
  313.       AddTail ((struct List *) &pu->p_zombies, (struct Node *) dm);
  314.     }
  315.  
  316.   _psignal ((struct Task *)mu->p_pptr, SIGCHLD);
  317.  
  318.   /* have to wakeup the parent `by hand' to make sure it gets
  319.      out of its sleep, since it might have SIGCHLD masked out or
  320.      ignored at the moment */
  321.   if (pu->p_stat == SSLEEP && pu->p_wchan == (caddr_t) pu)
  322.     ix_wakeup ((u_int)pu);
  323.  
  324.   if (mu->p_vfork_msg)
  325.     ReplyMsg ((struct Message *) mu->p_vfork_msg);
  326.  
  327.   /* this is necessary for process synchronisation, this process
  328.      will be unlinked from the process chain by wait4(), which will
  329.      also take care of reparenting the process if it was PT_ATTACHed
  330.       by a debugger */
  331.   if (dm)
  332.     ix_sleep ((caddr_t)dm, "vfork-dm");
  333. }
  334.  
  335. /* This function is used by vfork_resume and execve. Perhaps it should be made
  336.    externally available? It causes the process to switch to its own malloc
  337.    list, and copies errno and environ into private space. */
  338. void
  339. vfork_own_malloc (void)
  340. {
  341.   /* use volatile here, or the compiler might do wrong `optimization' .. */
  342.   volatile struct user *p = &u;
  343.  
  344.   if (p->u_mdp != &p->u_md)
  345.     {
  346.       char **parent_environ = *p->u_environ;
  347.       
  348.       /* switch to our memory list (which is initialized by OpenLibrary) */
  349.       p->u_mdp = (void *)&p->u_md;
  350.       /* dupvec now uses malloc() on our list */
  351.       p->u_environ = (char ***) malloc (4);
  352.       *p->u_environ = dupvec (parent_environ);
  353.       p->u_errno = (int *) malloc (4);
  354.       *p->u_errno = 0;
  355.       p->u_h_errno = (int *) malloc (4);
  356.       *p->u_h_errno = 0;
  357.     }
  358. }
  359.  
  360.  
  361. asm ("
  362.     .globl _vfork
  363.     .globl _vfork2
  364.     .globl _vfork_resume
  365. _vfork:
  366.     | store a setjmp () compatible frame on the stack to pass to _vfork ()
  367.     lea    sp@(-18*4),sp        | _JBLEN (17) longs on the stack
  368.     pea    sp@
  369.     jbsr    _setjmp
  370.     addqw    #4,sp
  371.     | now patch sp and pc, since they differ
  372.     addl    #20*4,sp@(8)        | account for buffer space
  373.     movel    sp@(18*4),sp@(20)    | insert real PC (return addr on stack)
  374.     | tell _vfork *not* yet to switch to own malloc-list
  375.     pea    0:W
  376.     bsr    __vfork
  377.     lea    sp@(18*4 + 4),sp
  378.     rts
  379.  
  380. _vfork2:
  381.     | this is the vfork used in older versions of the library
  382.     lea    sp@(-18*4),sp        | _JBLEN (17) longs on the stack
  383.     pea    sp@
  384.     jbsr    _setjmp
  385.     addqw    #4,sp
  386.     | now patch sp and pc, since they differ
  387.     addl    #20*4,sp@(8)        | account for buffer space
  388.     movel    sp@(18*4),sp@(20)    | insert real PC (return addr on stack)
  389.     | tell _vfork to have the child run with own malloc-list.
  390.     pea    1:W
  391.     bsr    __vfork
  392.     lea    sp@(18*4 + 4),sp
  393.     rts
  394.  
  395.  
  396.     | the following is longjmp(), with the subtle difference that this
  397.     | thing doesn't insist in returning something non-zero... 
  398. _vfork_longjmp:
  399.     movel    sp@(4),a0    /* save area pointer */
  400.     tstl    a0@(8)        /* ensure non-zero SP */
  401.     jeq    Lbotch        /* oops! */
  402.     movel    sp@(8),d0    /* grab return value */
  403.     moveml    a0@(28),d2-d7/a2-a4/a6    /* restore non-scratch regs */
  404.     movel    a0,sp@-        /* let sigreturn */
  405.     jbsr    _sigreturn    /*   finish for us */
  406.  
  407. Lbotch:
  408.     jsr    _longjmperror
  409.     stop    #0
  410.  
  411. _vfork_resume:
  412.     pea    sp@        | pass the sp containing the return address
  413.     bsr    __vfork_resume
  414.     addqw    #4,sp
  415.     rts
  416. ");
  417.  
  418. /*
  419.  * this is an implementation extension to the `real' vfork2(). Normally you
  420.  * can only cause the parent to resume by calling _exit() or execve() from
  421.  * the child. Since I can't provide a real fork() on the Amiga, this function
  422.  * is a third possibility to make the parent resume. You have then two
  423.  * concurrent processes sharing the same frame and global data... Please be
  424.  * EXTREMELY careful what you may do and what not. vfork2() itself is a hack,
  425.  * this is an even greater one...
  426.  *
  427.  * DO NOT use this function in combination with vfork(), or you'll get a big
  428.  * memory leak. Only use it with vfork2().
  429.  */
  430.  
  431. /*
  432.  * don't make this function static, gcc doesn't notice that it's used from the
  433.  * assembly code above, so make sure it isn't optimized away...
  434.  */
  435. void
  436. _vfork_resume (u_int *copy_from_sp)
  437. {
  438.   struct vfork_msg **vm = &u.p_vfork_msg;
  439.  
  440.   if (*vm)
  441.     {
  442.       u_int *sp = (u_int *)u.u_save_sp;
  443.       u_int *copy_till_sp = (u_int *)get_sp ();
  444.  
  445.       /* be sure to switch to our memory list */
  446.       vfork_own_malloc ();
  447.  
  448.       /* copy the stack frame */
  449.       copy_from_sp++;
  450.       do
  451.     *--sp = *--copy_from_sp;
  452.       while (copy_from_sp > copy_till_sp);
  453.  
  454.       set_sp ((u_int) sp);
  455.  
  456.       ReplyMsg ((struct Message *) *vm);
  457.       *vm = 0;
  458.     }
  459. }
  460.  
  461. #define STACKNAME ("IXSTACK")    /* Environment variable for stack size */
  462.  
  463. static int get_stack_size(struct Process *proc)
  464. {
  465.   struct CommandLineInterface *CLI;
  466.   int stack_size;
  467.   char *tmp;
  468.  
  469.   CLI = BTOCPTR (proc->pr_CLI);
  470.   if ((tmp = getenv (STACKNAME)))
  471.     stack_size = atoi (tmp);
  472.   else
  473.     stack_size = CLI ? CLI->cli_DefaultStack * 4 : proc->pr_StackSize;
  474.   if (stack_size < STACKSIZE)
  475.     return STACKSIZE;
  476.   return stack_size;
  477. }
  478.  
  479. /*
  480.  * don't make this function static, gcc doesn't notice that it's used from the
  481.  * assembly code above, so make sure it isn't optimized away...
  482.  */
  483. int
  484. _vfork (int own_malloc, struct reg_parms rp)
  485. {
  486.   struct Process *me = (struct Process *) FindTask(0);
  487.   struct CommandLineInterface *CLI = (void *)(me->pr_CLI);
  488.   u_int stack_size = get_stack_size(me);
  489.   /* those *have* to be in registers to survive the stack deallocation */
  490.   register struct vfork_msg *vm asm ("a2");
  491.   register int omask asm ("d2");
  492.   register struct Process *child asm ("a3");
  493.   struct TagItem tags [] = {
  494.     { NP_Entry, (ULONG) launcher, },
  495.     { NP_Cli, (ULONG) (CLI ? -1 : 0), },    /* same thing we are */
  496.     { NP_Name, (ULONG) "vfork()'d process", },    /* to be overridden by execve() */
  497.     { NP_StackSize, stack_size, },        /* same size we use */
  498.     { TAG_END, 0, }
  499.   };
  500.   KPRINTF (("vfork: creating child with stacksize = $%lx[$%lx,$%lx]\n", stack_size, tags[3]));
  501.   vm = (struct vfork_msg *) kmalloc (sizeof (struct vfork_msg));
  502.   if (! vm)
  503.     {
  504.       errno = ENOMEM;
  505.       KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  506.       return -1;
  507.     }
  508.  
  509.   vm->vm_msg.mn_ReplyPort = u.u_sync_mp;
  510.   vm->vm_msg.mn_Node.ln_Type = NT_MESSAGE;
  511.   vm->vm_msg.mn_Length = sizeof (struct vfork_msg);
  512.   vm->vm_pptr = me;
  513.   vm->vm_own_malloc = own_malloc;
  514.   vm->vm_regs = & rp;
  515.  
  516.   /* we have to block all signals as long as the child uses our resources.
  517.    * but since the child needs to start with the signal mask BEFORE this
  518.    * general blocking, we have to pass it the old signal mask. This is a
  519.    * way to do it */
  520.   vm->vm_rc   = 
  521.   omask      = syscall (SYS_sigsetmask, ~0);
  522.  
  523.   /* save the passed frame in our user structure, since the child will
  524.      deallocate it from the stack when it `returns' to user code */
  525.   bcopy (&rp, &u.u_vfork_frame, sizeof (rp));
  526.  
  527.   KPRINTF (("vfork: creating child with stacksize = $%lx[$%lx,$%lx]\n", stack_size, tags[3]));
  528.   child = CreateNewProc (tags);
  529.  
  530.  
  531.   if (! child)
  532.     {
  533.       /* do I have to close input/output here? Or does the startup close
  534.          them no matter whether it succeeds or not ? */
  535.       kfree (vm);
  536.       syscall (SYS_sigsetmask, omask);
  537.       errno = EPROCLIM;
  538.       KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  539.       return -1;
  540.     }
  541.  
  542.   /* As soon as this message is dispatched, the child will `return' and 
  543.      deallocate the stack we're running on. So afterwards, *only* use
  544.      register variables and then longjmp () back.
  545.      Since we don't have a stack until after the longjmp(), temporarily
  546.      switch to our mini-stack */
  547.   set_sp ((u_int) &u.u_mini_stack[sizeof (u.u_mini_stack) / sizeof (long)]);
  548.  
  549.   vm->vm_self = child;
  550.   PutMsg (&child->pr_MsgPort, (struct Message *) vm);
  551.   /* wait until the child does execve() or _exit() */
  552.   WaitPort (u.u_sync_mp);
  553.   GetMsg (u.u_sync_mp);
  554.   syscall (SYS_sigsetmask, omask);
  555.  
  556.   if (vm->vm_rc)
  557.     {
  558.       errno = (int) vm->vm_rc;
  559.       KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  560.       child = (struct Process *) -1;
  561.     }
  562.       
  563.   /* this is the parent return, so we pass the id of the new child */
  564.   kfree (vm);
  565.   /* could use longjmp() here, but since we already *have* the local one.. */
  566.   vfork_longjmp (u.u_vfork_frame, (int) child);
  567.   return 0;  /* not reached */
  568. }
  569.  
  570. void ruadd(struct rusage *ru, struct rusage *ru2)
  571. {
  572.     register long *ip, *ip2;
  573.     register int i;
  574.  
  575.     timevaladd(&ru->ru_utime, &ru2->ru_utime);
  576.     timevaladd(&ru->ru_stime, &ru2->ru_stime);
  577.     if (ru->ru_maxrss < ru2->ru_maxrss)
  578.         ru->ru_maxrss = ru2->ru_maxrss;
  579.     ip = &ru->ru_first; ip2 = &ru2->ru_first;
  580.     for (i = &ru->ru_last - &ru->ru_first; i > 0; i--)
  581.         *ip++ += *ip2++;
  582. }
  583.  
  584. /*
  585.  * make process 'parent' the new parent of process 'child'.
  586.  */
  587. void
  588. proc_reparent(struct Process *child, struct Process *parent)
  589. {
  590.     register struct Process *o;
  591.     register struct Process *y;
  592.     struct user *cu = getuser(child);
  593.     struct user *pu = (parent != (struct Process *)1 ? getuser(parent) : 0);
  594.  
  595.     if (cu->p_pptr == parent)
  596.       return;
  597.  
  598.     /* fix up the child linkage for the old parent */
  599.     o = cu->p_osptr;
  600.     y = cu->p_ysptr;
  601.     if (y)
  602.       getuser(y)->p_osptr = o;
  603.     if (o)
  604.       getuser(o)->p_ysptr = y;
  605.     if (cu->p_pptr && cu->p_pptr != (struct Process *)1)
  606.       if (getuser(cu->p_pptr)->p_cptr == child)
  607.         getuser(cu->p_pptr)->p_cptr = o;
  608.  
  609.         if (pu)
  610.           {
  611.         /* fix up child linkage for new parent, if there is one */
  612.         o = pu->p_cptr;
  613.         if (o)
  614.           getuser(o)->p_ysptr = child;
  615.         cu->p_osptr = o;
  616.         cu->p_ysptr = NULL;
  617.         pu->p_cptr = child;
  618.         cu->p_pptr = parent;
  619.       }
  620. }
  621.  
  622. /* This function is in desperate need of redesign !!!! */
  623.  
  624. int
  625. wait4 (int pid, int *status, int options, struct rusage *rusage)
  626. {
  627.   struct Process *me = (struct Process *) FindTask (0);
  628.   struct user *mu = getuser(me);
  629.  
  630.   for (;;)
  631.     {
  632.       int err = 0;
  633.       struct death_msg *dm, *ndm;
  634.       int got_node = 0;
  635.  
  636.       Forbid ();
  637.       
  638.       for (dm = (struct death_msg *) mu->p_zombies.mlh_Head;
  639.          (ndm = (struct death_msg *) dm->dm_node.mln_Succ);
  640.          dm = ndm)
  641.       if (pid == -1 ||
  642.           (pid == 0 && dm->dm_pgrp == mu->p_pgrp) ||
  643.         (pid < -1 && dm->dm_pgrp == - pid) ||
  644.         (pid == (int) dm->dm_child))
  645.       {
  646.         got_node = 1;
  647.         break;
  648.       }
  649.  
  650.       if (!got_node && !mu->p_cptr)
  651.     err = ECHILD;
  652.  
  653.       if (got_node)
  654.     /* Handle exited children first.  */
  655.         {
  656.       struct Process *child = dm->dm_child;
  657.       struct user *cu = getuser(child);
  658.       struct Task *t;
  659.  
  660.       /*
  661.        * If we got the child through a ptrace 'attach',
  662.        * we need to give it back to the old parent.
  663.        */
  664.       if (cu->p_opptr && (t = pfind((pid_t)cu->p_opptr)))
  665.         {
  666.           proc_reparent(child, (struct Process *)t);
  667.           cu->p_opptr = 0;
  668.           _psignal(t, SIGCHLD);
  669.           ix_wakeup((u_int)t);
  670.           Permit();
  671.           return (int)child;
  672.         }
  673.       KPRINTF (("wait4: unlinking child $%lx\n", dm->dm_child));
  674.       Remove ((struct Node *) dm);
  675.       
  676.       if (cu->p_ysptr)
  677.         getuser(cu->p_ysptr)->p_osptr = cu->p_osptr;
  678.  
  679.       if (cu->p_osptr)
  680.         getuser(cu->p_osptr)->p_ysptr = cu->p_ysptr;
  681.  
  682.           if (cu->p_pptr && cu->p_pptr != (struct Process *)1)
  683.         if (getuser(cu->p_pptr)->p_cptr == child)
  684.           getuser(cu->p_pptr)->p_cptr = cu->p_osptr;
  685.  
  686.       ix_wakeup ((u_int)dm);
  687.       Permit ();
  688.  
  689.           if (status)
  690.           {
  691.             *status = dm->dm_status;
  692.           }  
  693.           if (rusage)
  694.             *rusage = dm->dm_rusage;
  695.  
  696.       kfree (dm);
  697.  
  698.           return (int) child;
  699.     }
  700.       else
  701.     /* No child processes have died for now.
  702.        Do we have a traced child process to handle?  */
  703.     {
  704.       struct Process *p;
  705.       struct user *pu;
  706.  
  707.       KPRINTF(("wait4: checking traced children, pid=%lx, mu->p_cptr=%lx\n",
  708.            pid, mu->p_cptr));
  709.  
  710.       for (p = mu->p_cptr;
  711.            p && getuser(p);
  712.            p = getuser(p)->p_osptr)
  713.         {
  714.           KPRINTF(("wait4: checking pid p=%lx\n", p));
  715.           if ((pu = getuser(p)))
  716.             if (pid == -1
  717.             || ((int) p) == pid
  718.             || pu->p_pgrp == -pid
  719.             || (pid == 0
  720.                 && mu->p_pgrp == pu->p_pgrp))
  721.           {        
  722.             KPRINTF(("wait4: pu->p_stat=%lx, pu->flag=%lx\n",
  723.                 pu->p_stat, pu->p_flag));
  724.             if (pu->p_stat == SSTOP
  725.                 && (pu->p_flag & SWTED) == 0
  726.                 && (pu->p_flag & STRC || options & WUNTRACED))
  727.               {
  728.                 KPRINTF(("wait4: SSTOPed; p_xstat=0x%lx, W_STOPCODEd=0x%lx\n",
  729.                 pu->p_xstat, W_STOPCODE (pu->p_xstat)));
  730.                 pu->p_flag |= SWTED;
  731.                 if (status)
  732.                   {
  733.                 *status = W_STOPCODE (pu->p_xstat);
  734.                     KPRINTF(("wait4: status was set to %ld\n", *status));
  735.                   }
  736.                 Permit ();
  737.                 return (int)p;
  738.               }
  739.           }
  740.         }
  741.     }
  742.  
  743.       if (!got_node && err)
  744.     {
  745.       Permit ();
  746.       errno = err;
  747.       KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  748.       return -1;
  749.     }
  750.  
  751.       if (options & WNOHANG)
  752.     {
  753.       Permit ();
  754.       return 0;
  755.     }
  756.  
  757.       KPRINTF (("wait4: waiting for SIGCHLD\n"));
  758.       ix_sleep ((caddr_t)mu, "wait4");
  759.       if (mu->p_sig)
  760.     err = EINTR;
  761.  
  762.       Permit ();
  763.       if (CURSIG (mu))
  764.         setrun ((struct Task *)me);
  765.     }
  766. }
  767.