about summary refs log tree commit diff
path: root/REORG.TODO/sysdeps/mach/hurd/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/sysdeps/mach/hurd/fork.c')
-rw-r--r--REORG.TODO/sysdeps/mach/hurd/fork.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/mach/hurd/fork.c b/REORG.TODO/sysdeps/mach/hurd/fork.c
new file mode 100644
index 0000000000..582273ee14
--- /dev/null
+++ b/REORG.TODO/sysdeps/mach/hurd/fork.c
@@ -0,0 +1,733 @@
+/* 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)