mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-28 23:34:53 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			704 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			704 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 1994,1995,1996,1997,1999,2001,2002,2004,2005,2006,2011
 | |
| 	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>
 | |
| 
 | |
| #undef __fork
 | |
| 
 | |
| 
 | |
| /* Things that want to be locked while forking.  */
 | |
| symbol_set_declare (_hurd_fork_locks)
 | |
| 
 | |
| 
 | |
| /* 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;
 | |
| 
 | |
|   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);
 | |
| 
 | |
|       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;
 | |
| 
 | |
| 
 | |
|       /* Stop all other threads while copying the address space,
 | |
| 	 so nothing changes.  */
 | |
|       err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
 | |
|       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,
 | |
| 						       ¬ify_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;
 | |
|       /* We have one extra user reference created at the beginning of this
 | |
| 	 function, accounted for by mach_port_names (and which will thus be
 | |
| 	 accounted for in the child below).  This extra right gets consumed
 | |
| 	 in the child by the store into _hurd_sigthread in the child fork.  */
 | |
|       if (thread_refs > 1 &&
 | |
| 	  (err = __mach_port_mod_refs (newtask, ss->thread,
 | |
| 				       MACH_PORT_RIGHT_SEND,
 | |
| 				       thread_refs)))
 | |
| 	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;
 | |
| #if 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));
 | |
| 	}
 | |
| 
 | |
|       /* 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);
 | |
| 
 | |
|       /* 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);
 | |
| 
 | |
|   return err ? __hurd_fail (err) : pid;
 | |
| }
 | |
| libc_hidden_def (__fork)
 | |
| 
 | |
| weak_alias (__fork, fork)
 |