Blame sysdeps/mach/hurd/fork.c

Packit 6c4009
/* Copyright (C) 1994-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <hurd.h>
Packit 6c4009
#include <hurd/signal.h>
Packit 6c4009
#include <hurd/threadvar.h>
Packit 6c4009
#include <setjmp.h>
Packit 6c4009
#include <thread_state.h>
Packit 6c4009
#include <sysdep.h>		/* For stack growth direction.  */
Packit 6c4009
#include "set-hooks.h"
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include "hurdmalloc.h"		/* XXX */
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include <malloc/malloc-internal.h>
Packit 6c4009
Packit 6c4009
#undef __fork
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Things that want to be locked while forking.  */
Packit 6c4009
symbol_set_declare (_hurd_fork_locks)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Application callbacks registered through pthread_atfork.  */
Packit 6c4009
DEFINE_HOOK (_hurd_atfork_prepare_hook, (void));
Packit 6c4009
DEFINE_HOOK (_hurd_atfork_child_hook, (void));
Packit 6c4009
DEFINE_HOOK (_hurd_atfork_parent_hook, (void));
Packit 6c4009
Packit 6c4009
/* Things that want to be called before we fork, to prepare the parent for
Packit 6c4009
   task_create, when the new child task will inherit our address space.  */
Packit 6c4009
DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
Packit 6c4009
Packit 6c4009
/* Things that want to be called when we are forking, with the above all
Packit 6c4009
   locked.  They are passed the task port of the child.  The child process
Packit 6c4009
   is all set up except for doing proc_child, and has no threads yet.  */
Packit 6c4009
DEFINE_HOOK (_hurd_fork_setup_hook, (void));
Packit 6c4009
Packit 6c4009
/* Things to be run in the child fork.  */
Packit 6c4009
DEFINE_HOOK (_hurd_fork_child_hook, (void));
Packit 6c4009
Packit 6c4009
/* Things to be run in the parent fork.  */
Packit 6c4009
DEFINE_HOOK (_hurd_fork_parent_hook, (void));
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Clone the calling process, creating an exact copy.
Packit 6c4009
   Return -1 for errors, 0 to the new process,
Packit 6c4009
   and the process ID of the new process to the old process.  */
Packit 6c4009
pid_t
Packit 6c4009
__fork (void)
Packit 6c4009
{
Packit 6c4009
  jmp_buf env;
Packit 6c4009
  pid_t pid;
Packit 6c4009
  size_t i;
Packit 6c4009
  error_t err;
Packit 6c4009
  struct hurd_sigstate *volatile ss;
Packit 6c4009
Packit 6c4009
  RUN_HOOK (_hurd_atfork_prepare_hook, ());
Packit 6c4009
Packit 6c4009
  ss = _hurd_self_sigstate ();
Packit 6c4009
  __spin_lock (&ss->critical_section_lock);
Packit 6c4009
Packit 6c4009
#undef	LOSE
Packit 6c4009
#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
Packit 6c4009
Packit 6c4009
  if (! setjmp (env))
Packit 6c4009
    {
Packit 6c4009
      process_t newproc;
Packit 6c4009
      task_t newtask;
Packit 6c4009
      thread_t thread, sigthread;
Packit 6c4009
      mach_port_urefs_t thread_refs, sigthread_refs;
Packit 6c4009
      struct machine_thread_state state;
Packit 6c4009
      mach_msg_type_number_t statecount;
Packit 6c4009
      mach_port_t *portnames = NULL;
Packit 6c4009
      mach_msg_type_number_t nportnames = 0;
Packit 6c4009
      mach_port_type_t *porttypes = NULL;
Packit 6c4009
      mach_msg_type_number_t nporttypes = 0;
Packit 6c4009
      thread_t *threads = NULL;
Packit 6c4009
      mach_msg_type_number_t nthreads = 0;
Packit 6c4009
      int ports_locked = 0, stopped = 0;
Packit 6c4009
Packit 6c4009
      void resume_threads (void)
Packit 6c4009
	{
Packit 6c4009
	  if (! stopped)
Packit 6c4009
	    return;
Packit 6c4009
Packit 6c4009
	  assert (threads);
Packit 6c4009
Packit 6c4009
	  for (i = 0; i < nthreads; ++i)
Packit 6c4009
	    if (threads[i] != ss->thread)
Packit 6c4009
	      __thread_resume (threads[i]);
Packit 6c4009
	  stopped = 0;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Run things that prepare for forking before we create the task.  */
Packit 6c4009
      RUN_HOOK (_hurd_fork_prepare_hook, ());
Packit 6c4009
Packit 6c4009
      /* Lock things that want to be locked before we fork.  */
Packit 6c4009
      {
Packit 6c4009
	void *const *p;
Packit 6c4009
	for (p = symbol_set_first_element (_hurd_fork_locks);
Packit 6c4009
	     ! symbol_set_end_p (_hurd_fork_locks, p);
Packit 6c4009
	     ++p)
Packit 6c4009
	  __mutex_lock (*p);
Packit 6c4009
      }
Packit 6c4009
      __mutex_lock (&_hurd_siglock);
Packit 6c4009
Packit 6c4009
      /* Acquire malloc locks.  This needs to come last because fork
Packit 6c4009
	 handlers may use malloc, and the libio list lock has an
Packit 6c4009
	 indirect malloc dependency as well (via the getdelim
Packit 6c4009
	 function).  */
Packit 6c4009
      call_function_static_weak (__malloc_fork_lock_parent);
Packit 6c4009
      _hurd_malloc_fork_prepare ();
Packit 6c4009
Packit 6c4009
      newtask = MACH_PORT_NULL;
Packit 6c4009
      thread = sigthread = MACH_PORT_NULL;
Packit 6c4009
      newproc = MACH_PORT_NULL;
Packit 6c4009
Packit 6c4009
      /* Lock all the port cells for the standard ports while we copy the
Packit 6c4009
	 address space.  We want to insert all the send rights into the
Packit 6c4009
	 child with the same names.  */
Packit 6c4009
      for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
	__spin_lock (&_hurd_ports[i].lock);
Packit 6c4009
      ports_locked = 1;
Packit 6c4009
Packit 6c4009
Packit 6c4009
      /* Keep our SS locked while stopping other threads, so they don't get a
Packit 6c4009
         chance to have it locked in the copied space.  */
Packit 6c4009
      __spin_lock (&ss->lock);
Packit 6c4009
      /* Stop all other threads while copying the address space,
Packit 6c4009
	 so nothing changes.  */
Packit 6c4009
      err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
Packit 6c4009
      __spin_unlock (&ss->lock);
Packit 6c4009
      if (!err)
Packit 6c4009
	{
Packit 6c4009
	  stopped = 1;
Packit 6c4009
Packit 6c4009
#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
Packit 6c4009
Packit 6c4009
#ifdef XXX_KERNEL_PAGE_FAULT_BUG
Packit 6c4009
	  /* Gag me with a pitchfork.
Packit 6c4009
	     The bug scenario is this:
Packit 6c4009
Packit 6c4009
	     - The page containing __mach_task_self_ is paged out.
Packit 6c4009
	     - The signal thread was faulting on that page when we
Packit 6c4009
	       suspended it via proc_dostop.  It holds some lock, or set
Packit 6c4009
	       some busy bit, or somesuch.
Packit 6c4009
	     - Now this thread faults on that same page.
Packit 6c4009
	     - GRATUIOUS DEADLOCK
Packit 6c4009
Packit 6c4009
	     We can break the deadlock by aborting the thread that faulted
Packit 6c4009
	     first, which if the bug happened was the signal thread because
Packit 6c4009
	     it is the only other thread and we just suspended it.
Packit 6c4009
	     */
Packit 6c4009
	  __thread_abort (_hurd_msgport_thread);
Packit 6c4009
#endif
Packit 6c4009
	  /* Create the child task.  It will inherit a copy of our memory.  */
Packit 6c4009
	  err = __task_create (__mach_task_self (),
Packit 6c4009
#ifdef KERN_INVALID_LEDGER
Packit 6c4009
			       NULL, 0,	/* OSF Mach */
Packit 6c4009
#endif
Packit 6c4009
			       1, &newtask);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Unlock the global signal state lock, so we do not
Packit 6c4009
	 block the signal thread any longer than necessary.  */
Packit 6c4009
      __mutex_unlock (&_hurd_siglock);
Packit 6c4009
Packit 6c4009
      if (err)
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Fetch the names of all ports used in this task.  */
Packit 6c4009
      if (err = __mach_port_names (__mach_task_self (),
Packit 6c4009
				   &portnames, &nportnames,
Packit 6c4009
				   &porttypes, &nporttypes))
Packit 6c4009
	LOSE;
Packit 6c4009
      if (nportnames != nporttypes)
Packit 6c4009
	{
Packit 6c4009
	  err = EGRATUITOUS;
Packit 6c4009
	  LOSE;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Get send rights for all the threads in this task.
Packit 6c4009
	 We want to avoid giving these rights to the child.  */
Packit 6c4009
      if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Get the child process's proc server port.  We will insert it into
Packit 6c4009
	 the child with the same name as we use for our own proc server
Packit 6c4009
	 port; and we will need it to set the child's message port.  */
Packit 6c4009
      if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
Packit 6c4009
				  newtask, &newproc))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Insert all our port rights into the child task.  */
Packit 6c4009
      thread_refs = sigthread_refs = 0;
Packit 6c4009
      for (i = 0; i < nportnames; ++i)
Packit 6c4009
	{
Packit 6c4009
	  if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
Packit 6c4009
	    {
Packit 6c4009
	      /* This is a receive right.  We want to give the child task
Packit 6c4009
		 its own new receive right under the same name.  */
Packit 6c4009
	      if (err = __mach_port_allocate_name (newtask,
Packit 6c4009
						   MACH_PORT_RIGHT_RECEIVE,
Packit 6c4009
						   portnames[i]))
Packit 6c4009
		LOSE;
Packit 6c4009
	      if (porttypes[i] & MACH_PORT_TYPE_SEND)
Packit 6c4009
		{
Packit 6c4009
		  /* Give the child as many send rights for its receive
Packit 6c4009
		     right as we have for ours.  */
Packit 6c4009
		  mach_port_urefs_t refs;
Packit 6c4009
		  mach_port_t port;
Packit 6c4009
		  mach_msg_type_name_t poly;
Packit 6c4009
		  if (err = __mach_port_get_refs (__mach_task_self (),
Packit 6c4009
						  portnames[i],
Packit 6c4009
						  MACH_PORT_RIGHT_SEND,
Packit 6c4009
						  &refs))
Packit 6c4009
		    LOSE;
Packit 6c4009
		  if (err = __mach_port_extract_right (newtask,
Packit 6c4009
						       portnames[i],
Packit 6c4009
						       MACH_MSG_TYPE_MAKE_SEND,
Packit 6c4009
						       &port, &poly))
Packit 6c4009
		    LOSE;
Packit 6c4009
		  if (portnames[i] == _hurd_msgport)
Packit 6c4009
		    {
Packit 6c4009
		      /* We just created a receive right for the child's
Packit 6c4009
			 message port and are about to insert send rights
Packit 6c4009
			 for it.  Now, while we happen to have a send right
Packit 6c4009
			 for it, give it to the proc server.  */
Packit 6c4009
		      mach_port_t old;
Packit 6c4009
		      if (err = __proc_setmsgport (newproc, port, &old))
Packit 6c4009
			LOSE;
Packit 6c4009
		      if (old != MACH_PORT_NULL)
Packit 6c4009
			/* XXX what to do here? */
Packit 6c4009
			__mach_port_deallocate (__mach_task_self (), old);
Packit 6c4009
		      /* The new task will receive its own exceptions
Packit 6c4009
			 on its message port.  */
Packit 6c4009
		      if (err =
Packit 6c4009
#ifdef TASK_EXCEPTION_PORT
Packit 6c4009
			  __task_set_special_port (newtask,
Packit 6c4009
						   TASK_EXCEPTION_PORT,
Packit 6c4009
						   port)
Packit 6c4009
#elif defined (EXC_MASK_ALL)
Packit 6c4009
			  __task_set_exception_ports
Packit 6c4009
			  (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
Packit 6c4009
						     | EXC_MASK_MACH_SYSCALL
Packit 6c4009
						     | EXC_MASK_RPC_ALERT),
Packit 6c4009
			   port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
Packit 6c4009
#else
Packit 6c4009
# error task_set_exception_port?
Packit 6c4009
#endif
Packit 6c4009
			  )
Packit 6c4009
			LOSE;
Packit 6c4009
		    }
Packit 6c4009
		  if (err = __mach_port_insert_right (newtask,
Packit 6c4009
						      portnames[i],
Packit 6c4009
						      port,
Packit 6c4009
						      MACH_MSG_TYPE_MOVE_SEND))
Packit 6c4009
		    LOSE;
Packit 6c4009
		  if (refs > 1 &&
Packit 6c4009
		      (err = __mach_port_mod_refs (newtask,
Packit 6c4009
						   portnames[i],
Packit 6c4009
						   MACH_PORT_RIGHT_SEND,
Packit 6c4009
						   refs - 1)))
Packit 6c4009
		    LOSE;
Packit 6c4009
		}
Packit 6c4009
	      if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
Packit 6c4009
		{
Packit 6c4009
		  /* Give the child a send-once right for its receive right,
Packit 6c4009
		     since we have one for ours.  */
Packit 6c4009
		  mach_port_t port;
Packit 6c4009
		  mach_msg_type_name_t poly;
Packit 6c4009
		  if (err = __mach_port_extract_right
Packit 6c4009
		      (newtask,
Packit 6c4009
		       portnames[i],
Packit 6c4009
		       MACH_MSG_TYPE_MAKE_SEND_ONCE,
Packit 6c4009
		       &port, &poly))
Packit 6c4009
		    LOSE;
Packit 6c4009
		  if (err = __mach_port_insert_right
Packit 6c4009
		      (newtask,
Packit 6c4009
		       portnames[i], port,
Packit 6c4009
		       MACH_MSG_TYPE_MOVE_SEND_ONCE))
Packit 6c4009
		    LOSE;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else if (porttypes[i] &
Packit 6c4009
		   (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
Packit 6c4009
	    {
Packit 6c4009
	      /* This is a send right or a dead name.
Packit 6c4009
		 Give the child as many references for it as we have.  */
Packit 6c4009
	      mach_port_urefs_t refs = 0, *record_refs = NULL;
Packit 6c4009
	      mach_port_t insert;
Packit 6c4009
	      mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
Packit 6c4009
	      if (portnames[i] == newtask || portnames[i] == newproc)
Packit 6c4009
		/* Skip the name we use for the child's task or proc ports.  */
Packit 6c4009
		continue;
Packit 6c4009
	      if (portnames[i] == __mach_task_self ())
Packit 6c4009
		/* For the name we use for our own task port,
Packit 6c4009
		   insert the child's task port instead.  */
Packit 6c4009
		insert = newtask;
Packit 6c4009
	      else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
Packit 6c4009
		{
Packit 6c4009
		  /* Use the proc server port for the new task.  */
Packit 6c4009
		  insert = newproc;
Packit 6c4009
		  insert_type = MACH_MSG_TYPE_COPY_SEND;
Packit 6c4009
		}
Packit 6c4009
	      else if (portnames[i] == ss->thread)
Packit 6c4009
		{
Packit 6c4009
		  /* For the name we use for our own thread port, we will
Packit 6c4009
		     insert the thread port for the child main user thread
Packit 6c4009
		     after we create it.  */
Packit 6c4009
		  insert = MACH_PORT_NULL;
Packit 6c4009
		  record_refs = &thread_refs;
Packit 6c4009
		  /* Allocate a dead name right for this name as a
Packit 6c4009
		     placeholder, so the kernel will not chose this name
Packit 6c4009
		     for any other new port (it might use it for one of the
Packit 6c4009
		     rights created when a thread is created).  */
Packit 6c4009
		  if (err = __mach_port_allocate_name
Packit 6c4009
		      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
Packit 6c4009
		    LOSE;
Packit 6c4009
		}
Packit 6c4009
	      else if (portnames[i] == _hurd_msgport_thread)
Packit 6c4009
		/* For the name we use for our signal thread's thread port,
Packit 6c4009
		   we will insert the thread port for the child's signal
Packit 6c4009
		   thread after we create it.  */
Packit 6c4009
		{
Packit 6c4009
		  insert = MACH_PORT_NULL;
Packit 6c4009
		  record_refs = &sigthread_refs;
Packit 6c4009
		  /* Allocate a dead name right as a placeholder.  */
Packit 6c4009
		  if (err = __mach_port_allocate_name
Packit 6c4009
		      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
Packit 6c4009
		    LOSE;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* Skip the name we use for any of our own thread ports.  */
Packit 6c4009
		  mach_msg_type_number_t j;
Packit 6c4009
		  for (j = 0; j < nthreads; ++j)
Packit 6c4009
		    if (portnames[i] == threads[j])
Packit 6c4009
		      break;
Packit 6c4009
		  if (j < nthreads)
Packit 6c4009
		    continue;
Packit 6c4009
Packit 6c4009
		  /* Copy our own send right.  */
Packit 6c4009
		  insert = portnames[i];
Packit 6c4009
		}
Packit 6c4009
	      /* Find out how many user references we have for
Packit 6c4009
		 the send right with this name.  */
Packit 6c4009
	      if (err = __mach_port_get_refs (__mach_task_self (),
Packit 6c4009
					      portnames[i],
Packit 6c4009
					      MACH_PORT_RIGHT_SEND,
Packit 6c4009
					      record_refs ?: &refs))
Packit 6c4009
		LOSE;
Packit 6c4009
	      if (insert == MACH_PORT_NULL)
Packit 6c4009
		continue;
Packit 6c4009
	      if (insert == portnames[i] &&
Packit 6c4009
		  (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
Packit 6c4009
		/* This is a dead name; allocate another dead name
Packit 6c4009
		   with the same name in the child.  */
Packit 6c4009
	      allocate_dead_name:
Packit 6c4009
		err = __mach_port_allocate_name (newtask,
Packit 6c4009
						 MACH_PORT_RIGHT_DEAD_NAME,
Packit 6c4009
						 portnames[i]);
Packit 6c4009
	      else
Packit 6c4009
		/* Insert the chosen send right into the child.  */
Packit 6c4009
		err = __mach_port_insert_right (newtask,
Packit 6c4009
						portnames[i],
Packit 6c4009
						insert, insert_type);
Packit 6c4009
	      switch (err)
Packit 6c4009
		{
Packit 6c4009
		case KERN_NAME_EXISTS:
Packit 6c4009
		  {
Packit 6c4009
		    /* It already has a send right under this name (?!).
Packit 6c4009
		       Well, it starts out with a send right for its task
Packit 6c4009
		       port, and inherits the bootstrap and exception ports
Packit 6c4009
		       from us.  */
Packit 6c4009
		    mach_port_t childport;
Packit 6c4009
		    mach_msg_type_name_t poly;
Packit 6c4009
		    assert (__mach_port_extract_right (newtask, portnames[i],
Packit 6c4009
						       MACH_MSG_TYPE_COPY_SEND,
Packit 6c4009
						       &childport,
Packit 6c4009
						       &poly) == 0 &&
Packit 6c4009
			    childport == insert &&
Packit 6c4009
			    __mach_port_deallocate (__mach_task_self (),
Packit 6c4009
						    childport) == 0);
Packit 6c4009
		    break;
Packit 6c4009
		  }
Packit 6c4009
Packit 6c4009
		case KERN_INVALID_CAPABILITY:
Packit 6c4009
		  /* The port just died.  It was a send right,
Packit 6c4009
		     and now it's a dead name.  */
Packit 6c4009
		  goto allocate_dead_name;
Packit 6c4009
Packit 6c4009
		default:
Packit 6c4009
		  LOSE;
Packit 6c4009
		  break;
Packit 6c4009
Packit 6c4009
		case KERN_SUCCESS:
Packit 6c4009
		  /* Give the child as many user references as we have.  */
Packit 6c4009
		  if (refs > 1 &&
Packit 6c4009
		      (err = __mach_port_mod_refs (newtask,
Packit 6c4009
						   portnames[i],
Packit 6c4009
						   MACH_PORT_RIGHT_SEND,
Packit 6c4009
						   refs - 1)))
Packit 6c4009
		    LOSE;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Unlock the standard port cells.  The child must unlock its own
Packit 6c4009
	 copies too.  */
Packit 6c4009
      for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
	__spin_unlock (&_hurd_ports[i].lock);
Packit 6c4009
      ports_locked = 0;
Packit 6c4009
Packit 6c4009
      /* All state has now been copied from the parent.  It is safe to
Packit 6c4009
	 resume other parent threads.  */
Packit 6c4009
      resume_threads ();
Packit 6c4009
Packit 6c4009
      /* Create the child main user thread and signal thread.  */
Packit 6c4009
      if ((err = __thread_create (newtask, &thread)) ||
Packit 6c4009
	  (err = __thread_create (newtask, &sigthread)))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Insert send rights for those threads.  We previously allocated
Packit 6c4009
	 dead name rights with the names we want to give the thread ports
Packit 6c4009
	 in the child as placeholders.  Now deallocate them so we can use
Packit 6c4009
	 the names.  */
Packit 6c4009
      if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
Packit 6c4009
	  (err = __mach_port_insert_right (newtask, ss->thread,
Packit 6c4009
					   thread, MACH_MSG_TYPE_COPY_SEND)))
Packit 6c4009
	LOSE;
Packit 6c4009
      if (thread_refs > 1 &&
Packit 6c4009
	  (err = __mach_port_mod_refs (newtask, ss->thread,
Packit 6c4009
				       MACH_PORT_RIGHT_SEND,
Packit 6c4009
				       thread_refs - 1)))
Packit 6c4009
	LOSE;
Packit 6c4009
      if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
Packit 6c4009
	  && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
Packit 6c4009
	      (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
Packit 6c4009
					       sigthread,
Packit 6c4009
					       MACH_MSG_TYPE_COPY_SEND))))
Packit 6c4009
	LOSE;
Packit 6c4009
      if (sigthread_refs > 1 &&
Packit 6c4009
	  (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
Packit 6c4009
				       MACH_PORT_RIGHT_SEND,
Packit 6c4009
				       sigthread_refs - 1)))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* This seems like a convenient juncture to copy the proc server's
Packit 6c4009
	 idea of what addresses our argv and envp are found at from the
Packit 6c4009
	 parent into the child.  Since we happen to know that the child
Packit 6c4009
	 shares our memory image, it is we who should do this copying.  */
Packit 6c4009
      {
Packit 6c4009
	vm_address_t argv, envp;
Packit 6c4009
	err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
Packit 6c4009
	       ?: __proc_set_arg_locations (newproc, argv, envp));
Packit 6c4009
	if (err)
Packit 6c4009
	  LOSE;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
      /* Set the child signal thread up to run the msgport server function
Packit 6c4009
	 using the same signal thread stack copied from our address space.
Packit 6c4009
	 We fetch the state before longjmp'ing it so that miscellaneous
Packit 6c4009
	 registers not affected by longjmp (such as i386 segment registers)
Packit 6c4009
	 are in their normal default state.  */
Packit 6c4009
      statecount = MACHINE_THREAD_STATE_COUNT;
Packit 6c4009
      if (err = __thread_get_state (_hurd_msgport_thread,
Packit 6c4009
				    MACHINE_THREAD_STATE_FLAVOR,
Packit 6c4009
				    (natural_t *) &state, &statecount))
Packit 6c4009
	LOSE;
Packit 6c4009
#ifdef STACK_GROWTH_UP
Packit 6c4009
      if (__hurd_sigthread_stack_base == 0)
Packit 6c4009
	{
Packit 6c4009
	  state.SP &= __hurd_threadvar_stack_mask;
Packit 6c4009
	  state.SP += __hurd_threadvar_stack_offset;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	state.SP = __hurd_sigthread_stack_base;
Packit 6c4009
#else
Packit 6c4009
      if (__hurd_sigthread_stack_end == 0)
Packit 6c4009
	{
Packit 6c4009
	  /* The signal thread has a stack assigned by cthreads.
Packit 6c4009
	     The threadvar_stack variables conveniently tell us how
Packit 6c4009
	     to get to the highest address in the stack, just below
Packit 6c4009
	     the per-thread variables.  */
Packit 6c4009
	  state.SP &= __hurd_threadvar_stack_mask;
Packit 6c4009
	  state.SP += __hurd_threadvar_stack_offset;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	state.SP = __hurd_sigthread_stack_end;
Packit 6c4009
#endif
Packit 6c4009
      MACHINE_THREAD_STATE_SET_PC (&state,
Packit 6c4009
				   (unsigned long int) _hurd_msgport_receive);
Packit 6c4009
Packit 6c4009
      /* Do special signal thread setup for TLS if needed.  */
Packit 6c4009
      if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
Packit 6c4009
				    (natural_t *) &state, statecount))
Packit 6c4009
	LOSE;
Packit 6c4009
      /* We do not thread_resume SIGTHREAD here because the child
Packit 6c4009
	 fork needs to do more setup before it can take signals.  */
Packit 6c4009
Packit 6c4009
      /* Set the child user thread up to return 1 from the setjmp above.  */
Packit 6c4009
      _hurd_longjmp_thread_state (&state, env, 1);
Packit 6c4009
Packit 6c4009
      /* Do special thread setup for TLS if needed.  */
Packit 6c4009
      if (err = _hurd_tls_fork (thread, ss->thread, &state))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
Packit 6c4009
				    (natural_t *) &state, statecount))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Get the PID of the child from the proc server.  We must do this
Packit 6c4009
	 before calling proc_child below, because at that point any
Packit 6c4009
	 authorized POSIX.1 process may kill the child task with SIGKILL.  */
Packit 6c4009
      if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* Register the child with the proc server.  It is important that
Packit 6c4009
	 this be that last thing we do before starting the child thread
Packit 6c4009
	 running.  Once proc_child has been done for the task, it appears
Packit 6c4009
	 as a POSIX.1 process.  Any errors we get must be detected before
Packit 6c4009
	 this point, and the child must have a message port so it responds
Packit 6c4009
	 to POSIX.1 signals.  */
Packit 6c4009
      if (err = __USEPORT (PROC, __proc_child (port, newtask)))
Packit 6c4009
	LOSE;
Packit 6c4009
Packit 6c4009
      /* This must be the absolutely last thing we do; we can't assume that
Packit 6c4009
	 the child will remain alive for even a moment once we do this.  We
Packit 6c4009
	 ignore errors because we have committed to the fork and are not
Packit 6c4009
	 allowed to return them after the process becomes visible to
Packit 6c4009
	 POSIX.1 (which happened right above when we called proc_child).  */
Packit 6c4009
      (void) __thread_resume (thread);
Packit 6c4009
Packit 6c4009
    lose:
Packit 6c4009
      if (ports_locked)
Packit 6c4009
	for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
	  __spin_unlock (&_hurd_ports[i].lock);
Packit 6c4009
Packit 6c4009
      resume_threads ();
Packit 6c4009
Packit 6c4009
      if (newtask != MACH_PORT_NULL)
Packit 6c4009
	{
Packit 6c4009
	  if (err)
Packit 6c4009
	    __task_terminate (newtask);
Packit 6c4009
	  __mach_port_deallocate (__mach_task_self (), newtask);
Packit 6c4009
	}
Packit 6c4009
      if (thread != MACH_PORT_NULL)
Packit 6c4009
	__mach_port_deallocate (__mach_task_self (), thread);
Packit 6c4009
      if (sigthread != MACH_PORT_NULL)
Packit 6c4009
	__mach_port_deallocate (__mach_task_self (), sigthread);
Packit 6c4009
      if (newproc != MACH_PORT_NULL)
Packit 6c4009
	__mach_port_deallocate (__mach_task_self (), newproc);
Packit 6c4009
Packit 6c4009
      if (portnames)
Packit 6c4009
	__vm_deallocate (__mach_task_self (),
Packit 6c4009
			 (vm_address_t) portnames,
Packit 6c4009
			 nportnames * sizeof (*portnames));
Packit 6c4009
      if (porttypes)
Packit 6c4009
	__vm_deallocate (__mach_task_self (),
Packit 6c4009
			 (vm_address_t) porttypes,
Packit 6c4009
			 nporttypes * sizeof (*porttypes));
Packit 6c4009
      if (threads)
Packit 6c4009
	{
Packit 6c4009
	  for (i = 0; i < nthreads; ++i)
Packit 6c4009
	    __mach_port_deallocate (__mach_task_self (), threads[i]);
Packit 6c4009
	  __vm_deallocate (__mach_task_self (),
Packit 6c4009
			   (vm_address_t) threads,
Packit 6c4009
			   nthreads * sizeof (*threads));
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Release malloc locks.  */
Packit 6c4009
      _hurd_malloc_fork_parent ();
Packit 6c4009
      call_function_static_weak (__malloc_fork_unlock_parent);
Packit 6c4009
Packit 6c4009
      /* Run things that want to run in the parent to restore it to
Packit 6c4009
	 normality.  Usually prepare hooks and parent hooks are
Packit 6c4009
	 symmetrical: the prepare hook arrests state in some way for the
Packit 6c4009
	 fork, and the parent hook restores the state for the parent to
Packit 6c4009
	 continue executing normally.  */
Packit 6c4009
      RUN_HOOK (_hurd_fork_parent_hook, ());
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      struct hurd_sigstate *oldstates;
Packit 6c4009
Packit 6c4009
      /* We are the child task.  Unlock the standard port cells, which were
Packit 6c4009
	 locked in the parent when we copied its memory.  The parent has
Packit 6c4009
	 inserted send rights with the names that were in the cells then.  */
Packit 6c4009
      for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
	__spin_unlock (&_hurd_ports[i].lock);
Packit 6c4009
Packit 6c4009
      /* We are one of the (exactly) two threads in this new task, we
Packit 6c4009
	 will take the task-global signals.  */
Packit 6c4009
      _hurd_sigthread = ss->thread;
Packit 6c4009
Packit 6c4009
      /* Claim our sigstate structure and unchain the rest: the
Packit 6c4009
	 threads existed in the parent task but don't exist in this
Packit 6c4009
	 task (the child process).  Delay freeing them until later
Packit 6c4009
	 because some of the further setup and unlocking might be
Packit 6c4009
	 required for free to work.  Before we finish cleaning up,
Packit 6c4009
	 we will reclaim the signal thread's sigstate structure (if
Packit 6c4009
	 it had one).  */
Packit 6c4009
      oldstates = _hurd_sigstates;
Packit 6c4009
      if (oldstates == ss)
Packit 6c4009
	oldstates = ss->next;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  while (_hurd_sigstates->next != ss)
Packit 6c4009
	    _hurd_sigstates = _hurd_sigstates->next;
Packit 6c4009
	  _hurd_sigstates->next = ss->next;
Packit 6c4009
	}
Packit 6c4009
      ss->next = NULL;
Packit 6c4009
      _hurd_sigstates = ss;
Packit 6c4009
      __mutex_unlock (&_hurd_siglock);
Packit 6c4009
Packit 6c4009
      /* Fetch our new process IDs from the proc server.  No need to
Packit 6c4009
	 refetch our pgrp; it is always inherited from the parent (so
Packit 6c4009
	 _hurd_pgrp is already correct), and the proc server will send us a
Packit 6c4009
	 proc_newids notification when it changes.  */
Packit 6c4009
      err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
Packit 6c4009
					     &_hurd_orphaned));
Packit 6c4009
Packit 6c4009
      /* Forking clears the trace flag.  */
Packit 6c4009
      __sigemptyset (&_hurdsig_traced);
Packit 6c4009
Packit 6c4009
      /* Release malloc locks.  */
Packit 6c4009
      _hurd_malloc_fork_child ();
Packit 6c4009
      call_function_static_weak (__malloc_fork_unlock_child);
Packit 6c4009
Packit 6c4009
      /* Run things that want to run in the child task to set up.  */
Packit 6c4009
      RUN_HOOK (_hurd_fork_child_hook, ());
Packit 6c4009
Packit 6c4009
      /* Set up proc server-assisted fault recovery for the signal thread.  */
Packit 6c4009
      _hurdsig_fault_init ();
Packit 6c4009
Packit 6c4009
      /* Start the signal thread listening on the message port.  */
Packit 6c4009
      if (!err)
Packit 6c4009
	err = __thread_resume (_hurd_msgport_thread);
Packit 6c4009
Packit 6c4009
      /* Reclaim the signal thread's sigstate structure and free the
Packit 6c4009
	 other old sigstate structures.  */
Packit 6c4009
      while (oldstates != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct hurd_sigstate *next = oldstates->next;
Packit 6c4009
Packit 6c4009
	  if (oldstates->thread == _hurd_msgport_thread)
Packit 6c4009
	    {
Packit 6c4009
	      /* If we have a second signal state structure then we
Packit 6c4009
		 must have been through here before--not good.  */
Packit 6c4009
	      assert (_hurd_sigstates->next == 0);
Packit 6c4009
	      _hurd_sigstates->next = oldstates;
Packit 6c4009
	      oldstates->next = 0;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    free (oldstates);
Packit 6c4009
Packit 6c4009
	  oldstates = next;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* XXX what to do if we have any errors here? */
Packit 6c4009
Packit 6c4009
      pid = 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Unlock things we locked before creating the child task.
Packit 6c4009
     They are locked in both the parent and child tasks.  */
Packit 6c4009
  {
Packit 6c4009
    void *const *p;
Packit 6c4009
    for (p = symbol_set_first_element (_hurd_fork_locks);
Packit 6c4009
	 ! symbol_set_end_p (_hurd_fork_locks, p);
Packit 6c4009
	 ++p)
Packit 6c4009
      __mutex_unlock (*p);
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  _hurd_critical_section_unlock (ss);
Packit 6c4009
Packit 6c4009
  if (!err)
Packit 6c4009
    {
Packit 6c4009
      if (pid != 0)
Packit 6c4009
	RUN_HOOK (_hurd_atfork_parent_hook, ());
Packit 6c4009
      else
Packit 6c4009
	RUN_HOOK (_hurd_atfork_child_hook, ());
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return err ? __hurd_fail (err) : pid;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__fork)
Packit 6c4009
Packit 6c4009
weak_alias (__fork, fork)