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