711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fork.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
 | |
|    2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <stdio.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include "cygerrno.h"
 | |
| #include "sigproc.h"
 | |
| #include "pinfo.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "dtable.h"
 | |
| #include "cygheap.h"
 | |
| #include "child_info.h"
 | |
| #include "cygtls.h"
 | |
| #include "tls_pbuf.h"
 | |
| #include "dll_init.h"
 | |
| #include "cygmalloc.h"
 | |
| #include "ntdll.h"
 | |
| 
 | |
| #define NPIDS_HELD 4
 | |
| 
 | |
| /* Timeout to wait for child to start, parent to init child, etc.  */
 | |
| /* FIXME: Once things stabilize, bump up to a few minutes.  */
 | |
| #define FORK_WAIT_TIMEOUT (300 * 1000)     /* 300 seconds */
 | |
| 
 | |
| class frok
 | |
| {
 | |
|   bool load_dlls;
 | |
|   child_info_fork ch;
 | |
|   const char *errmsg;
 | |
|   int child_pid;
 | |
|   int this_errno;
 | |
|   HANDLE hchild;
 | |
|   int __stdcall parent (volatile char * volatile here);
 | |
|   int __stdcall child (volatile char * volatile here);
 | |
|   bool error (const char *fmt, ...);
 | |
|   friend int fork ();
 | |
| };
 | |
| 
 | |
| static void
 | |
| resume_child (HANDLE forker_finished)
 | |
| {
 | |
|   SetEvent (forker_finished);
 | |
|   debug_printf ("signalled child");
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /* Notify parent that it is time for the next step. */
 | |
| static void __stdcall
 | |
| sync_with_parent (const char *s, bool hang_self)
 | |
| {
 | |
|   debug_printf ("signalling parent: %s", s);
 | |
|   fork_info->ready (false);
 | |
|   if (hang_self)
 | |
|     {
 | |
|       HANDLE h = fork_info->forker_finished;
 | |
|       /* Wait for the parent to fill in our stack and heap.
 | |
| 	 Don't wait forever here.  If our parent dies we don't want to clog
 | |
| 	 the system.  If the wait fails, we really can't continue so exit.  */
 | |
|       DWORD psync_rc = WaitForSingleObject (h, FORK_WAIT_TIMEOUT);
 | |
|       debug_printf ("awake");
 | |
|       switch (psync_rc)
 | |
| 	{
 | |
| 	case WAIT_TIMEOUT:
 | |
| 	  api_fatal ("WFSO timed out %s", s);
 | |
| 	  break;
 | |
| 	case WAIT_FAILED:
 | |
| 	  if (GetLastError () == ERROR_INVALID_HANDLE &&
 | |
| 	      WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
 | |
| 	    break;
 | |
| 	  api_fatal ("WFSO failed %s, fork_finished %p, %E", s,
 | |
| 		     fork_info->forker_finished);
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  debug_printf ("no problems");
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool
 | |
| frok::error (const char *fmt, ...)
 | |
| {
 | |
|   DWORD exit_code = ch.exit_code;
 | |
|   if (!exit_code && hchild)
 | |
|     {
 | |
|       exit_code = ch.proc_retry (hchild);
 | |
|       if (!exit_code)
 | |
| 	return false;
 | |
|     }
 | |
|   if (exit_code != EXITCODE_FORK_FAILED)
 | |
|     {
 | |
|       va_list ap;
 | |
|       static char buf[NT_MAX_PATH + 256];
 | |
|       va_start (ap, fmt);
 | |
|       __small_vsprintf (buf, fmt, ap);
 | |
|       errmsg = buf;
 | |
|     }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* Set up a pipe which will track the life of a "pid" through
 | |
|    even after we've exec'ed.  */
 | |
| void
 | |
| child_info::prefork (bool detached)
 | |
| {
 | |
|   if (!detached)
 | |
|     {
 | |
|       if (!CreatePipe (&rd_proc_pipe, &wr_proc_pipe, &sec_none_nih, 16))
 | |
| 	api_fatal ("prefork: couldn't create pipe process tracker, %E");
 | |
| 
 | |
|       if (!SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT,
 | |
| 				 HANDLE_FLAG_INHERIT))
 | |
| 	api_fatal ("prefork: couldn't set process pipe(%p) inherit state, %E",
 | |
| 		   wr_proc_pipe);
 | |
|       ProtectHandle1 (rd_proc_pipe, rd_proc_pipe);
 | |
|       ProtectHandle1 (wr_proc_pipe, wr_proc_pipe);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int __stdcall
 | |
| frok::child (volatile char * volatile here)
 | |
| {
 | |
|   HANDLE& hParent = ch.parent;
 | |
|   extern void fixup_hooks_after_fork ();
 | |
|   extern void fixup_timers_after_fork ();
 | |
| 
 | |
|   /* NOTE: Logically this belongs in dll_list::load_after_fork, but by
 | |
|      doing it here, before the first sync_with_parent, we can exploit
 | |
|      the existing retry mechanism in hopes of getting a more favorable
 | |
|      address space layout next time. */
 | |
|   dlls.reserve_space ();
 | |
| 
 | |
|   sync_with_parent ("after longjmp", true);
 | |
|   debug_printf ("child is running.  pid %d, ppid %d, stack here %p",
 | |
| 		myself->pid, myself->ppid, __builtin_frame_address (0));
 | |
|   sigproc_printf ("hParent %p, load_dlls %d", hParent, load_dlls);
 | |
| 
 | |
|   /* If we've played with the stack, stacksize != 0.  That means that
 | |
|      fork() was invoked from other than the main thread.  Make sure that
 | |
|      the threadinfo information is properly set up.  */
 | |
|   if (fork_info->stackaddr)
 | |
|     {
 | |
|       _main_tls = &_my_tls;
 | |
|       _main_tls->init_thread (NULL, NULL);
 | |
|       _main_tls->local_clib = *_impure_ptr;
 | |
|       _impure_ptr = &_main_tls->local_clib;
 | |
|     }
 | |
| 
 | |
|   set_cygwin_privileges (hProcToken);
 | |
|   clear_procimptoken ();
 | |
|   cygheap->user.reimpersonate ();
 | |
| 
 | |
| #ifdef DEBUGGING
 | |
|   if (GetEnvironmentVariableA ("FORKDEBUG", NULL, 0))
 | |
|     try_to_debug ();
 | |
|   char buf[80];
 | |
|   /* This is useful for debugging fork problems.  Use gdb to attach to
 | |
|      the pid reported here. */
 | |
|   if (GetEnvironmentVariableA ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
 | |
|     {
 | |
|       small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
 | |
|       Sleep (atoi (buf));
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
| 
 | |
|   /* Incredible but true:  If we use sockets and SYSV IPC shared memory,
 | |
|      there's a good chance that a duplicated socket in the child occupies
 | |
|      memory which is needed to duplicate shared memory from the parent
 | |
|      process, if the shared memory hasn't been duplicated already.
 | |
|      The same goes very likely for "normal" mmap shared memory, too, but
 | |
|      with SYSV IPC it was the first time observed.  So, *never* fixup
 | |
|      fdtab before fixing up shared memory. */
 | |
|   if (fixup_shms_after_fork ())
 | |
|     api_fatal ("recreate_shm areas after fork failed");
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
| 
 | |
|   /* If we haven't dynamically loaded any dlls, just signal
 | |
|      the parent.  Otherwise, load all the dlls, tell the parent
 | |
|       that we're done, and wait for the parent to fill in the.
 | |
|       loaded dlls' data/bss. */
 | |
|   if (!load_dlls)
 | |
|     {
 | |
|       cygheap->fdtab.fixup_after_fork (hParent);
 | |
|       sync_with_parent ("performed fork fixup", false);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       dlls.load_after_fork (hParent);
 | |
|       cygheap->fdtab.fixup_after_fork (hParent);
 | |
|       sync_with_parent ("loaded dlls", true);
 | |
|     }
 | |
| 
 | |
|   init_console_handler (myself->ctty > 0);
 | |
|   ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
 | |
| 
 | |
|   pthread::atforkchild ();
 | |
|   fixup_timers_after_fork ();
 | |
|   cygbench ("fork-child");
 | |
|   ld_preload ();
 | |
|   fixup_hooks_after_fork ();
 | |
|   _my_tls.fixup_after_fork ();
 | |
|   /* Clear this or the destructor will close them.  In the case of
 | |
|      rd_proc_pipe that would be an invalid handle.  In the case of
 | |
|      wr_proc_pipe it would be == my_wr_proc_pipe.  Both would be bad. */
 | |
|   ch.rd_proc_pipe = ch.wr_proc_pipe = NULL;
 | |
|   cygwin_finished_initializing = true;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #define NO_SLOW_PID_REUSE
 | |
| #ifndef NO_SLOW_PID_REUSE
 | |
| static void
 | |
| slow_pid_reuse (HANDLE h)
 | |
| {
 | |
|   static NO_COPY HANDLE last_fork_procs[NPIDS_HELD];
 | |
|   static NO_COPY unsigned nfork_procs;
 | |
| 
 | |
|   if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0])))
 | |
|     nfork_procs = 0;
 | |
|   /* Keep a list of handles to child processes sitting around to prevent
 | |
|      Windows from reusing the same pid n times in a row.  Having the same pids
 | |
|      close in succesion confuses bash.  Keeping a handle open will stop
 | |
|      windows from reusing the same pid.  */
 | |
|   if (last_fork_procs[nfork_procs])
 | |
|     ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
 | |
|   if (DuplicateHandle (GetCurrentProcess (), h,
 | |
| 		       GetCurrentProcess (), &last_fork_procs[nfork_procs],
 | |
| 		       0, FALSE, DUPLICATE_SAME_ACCESS))
 | |
|     ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
 | |
|   else
 | |
|     {
 | |
|       last_fork_procs[nfork_procs] = NULL;
 | |
|       system_printf ("couldn't create last_fork_proc, %E");
 | |
|     }
 | |
|   nfork_procs++;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int __stdcall
 | |
| frok::parent (volatile char * volatile stack_here)
 | |
| {
 | |
|   HANDLE forker_finished;
 | |
|   DWORD rc;
 | |
|   child_pid = -1;
 | |
|   this_errno = 0;
 | |
|   bool fix_impersonation = false;
 | |
|   pinfo child;
 | |
| 
 | |
|   int c_flags = GetPriorityClass (GetCurrentProcess ());
 | |
|   debug_printf ("priority class %d", c_flags);
 | |
|   /* Per MSDN, this must be specified even if lpEnvironment is set to NULL,
 | |
|      otherwise UNICODE characters in the parent environment are not copied
 | |
|      correctly to the child.  Omitting it may scramble %PATH% on non-English
 | |
|      systems. */
 | |
|   c_flags |= CREATE_UNICODE_ENVIRONMENT;
 | |
| 
 | |
|   errmsg = NULL;
 | |
|   hchild = NULL;
 | |
| 
 | |
|   /* If we don't have a console, then don't create a console for the
 | |
|      child either.  */
 | |
|   HANDLE console_handle = CreateFile ("CONOUT$", GENERIC_WRITE,
 | |
| 				      FILE_SHARE_READ | FILE_SHARE_WRITE,
 | |
| 				      &sec_none_nih, OPEN_EXISTING,
 | |
| 				      FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (console_handle != INVALID_HANDLE_VALUE)
 | |
|     CloseHandle (console_handle);
 | |
|   else
 | |
|     c_flags |= DETACHED_PROCESS;
 | |
| 
 | |
|   /* Some file types (currently only sockets) need extra effort in the
 | |
|      parent after CreateProcess and before copying the datastructures
 | |
|      to the child. So we have to start the child in suspend state,
 | |
|      unfortunately, to avoid a race condition. */
 | |
|   if (cygheap->fdtab.need_fixup_before ())
 | |
|     c_flags |= CREATE_SUSPENDED;
 | |
| 
 | |
|   /* Remember if we need to load dynamically linked dlls.
 | |
|      We do this here so that this information will be available
 | |
|      in the parent and, when the stack is copied, in the child. */
 | |
|   load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
 | |
| 
 | |
|   forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
 | |
|   if (forker_finished == NULL)
 | |
|     {
 | |
|       this_errno = geterrno_from_win_error ();
 | |
|       error ("unable to allocate forker_finished event");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   ProtectHandleINH (forker_finished);
 | |
| 
 | |
|   ch.forker_finished = forker_finished;
 | |
| 
 | |
|   PTEB teb = NtCurrentTeb ();
 | |
|   ch.stackbottom = _tlsbase;
 | |
|   ch.stacktop = (void *) _tlstop;
 | |
|   ch.stackaddr = teb->DeallocationStack;
 | |
|   ch.guardsize = 0;
 | |
|   if (&_my_tls != _main_tls)
 | |
|     {
 | |
|       /* We have not been started from the main thread.  Fetch the
 | |
| 	 information required to set up the thread stack identically
 | |
| 	 in the child. */
 | |
|       if (!ch.stackaddr)
 | |
| 	{
 | |
| 	  /* Pthread with application-provided stack.  Don't set up a
 | |
| 	     PAGE_GUARD page.  guardsize == -1 is used in alloc_stack_hard_way
 | |
| 	     to recognize this type of stack. */
 | |
| 	  ch.stackaddr = _my_tls.tid->attr.stackaddr;
 | |
| 	  ch.guardsize = (size_t) -1;
 | |
| 	}
 | |
|       else if (_my_tls.tid)
 | |
| 	/* If it's a pthread, fetch guardsize from thread attributes. */
 | |
| 	ch.guardsize = _my_tls.tid->attr.guardsize;
 | |
|     }
 | |
|   debug_printf ("stack - bottom %p, top %p, addr %p, guardsize %ly",
 | |
| 		ch.stackbottom, ch.stacktop, ch.stackaddr, ch.guardsize);
 | |
| 
 | |
|   PROCESS_INFORMATION pi;
 | |
|   STARTUPINFOW si;
 | |
| 
 | |
|   memset (&si, 0, sizeof (si));
 | |
|   si.cb = sizeof si;
 | |
| 
 | |
|   si.lpReserved2 = (LPBYTE) &ch;
 | |
|   si.cbReserved2 = sizeof (ch);
 | |
| 
 | |
|   syscall_printf ("CreateProcessW (%W, %W, 0, 0, 1, %y, 0, 0, %p, %p)",
 | |
| 		  myself->progname, myself->progname, c_flags, &si, &pi);
 | |
|   bool locked = __malloc_lock ();
 | |
| 
 | |
|   /* Remove impersonation */
 | |
|   cygheap->user.deimpersonate ();
 | |
|   fix_impersonation = true;
 | |
|   ch.refresh_cygheap ();
 | |
|   ch.prefork ();	/* set up process tracking pipes. */
 | |
| 
 | |
|   while (1)
 | |
|     {
 | |
|       hchild = NULL;
 | |
|       rc = CreateProcessW (myself->progname,	/* image to run */
 | |
| 			   GetCommandLineW (),	/* Take same space for command
 | |
| 						   line as in parent to make
 | |
| 						   sure child stack is allocated
 | |
| 						   in the same memory location
 | |
| 						   as in parent. */
 | |
| 			   &sec_none_nih,
 | |
| 			   &sec_none_nih,
 | |
| 			   TRUE,		/* inherit handles from parent */
 | |
| 			   c_flags,
 | |
| 			   NULL,		/* environment filled in later */
 | |
| 			   0,	  		/* use current drive/directory */
 | |
| 			   &si,
 | |
| 			   &pi);
 | |
| 
 | |
|       if (rc)
 | |
| 	debug_printf ("forked pid %u", pi.dwProcessId);
 | |
|       else
 | |
| 	{
 | |
| 	  this_errno = geterrno_from_win_error ();
 | |
| 	  error ("CreateProcessW failed for '%W'", myself->progname);
 | |
| 	  memset (&pi, 0, sizeof (pi));
 | |
| 	  goto cleanup;
 | |
| 	}
 | |
| 
 | |
|       if (cygheap->fdtab.need_fixup_before ())
 | |
| 	{
 | |
| 	  cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
 | |
| 	  ResumeThread (pi.hThread);
 | |
| 	}
 | |
| 
 | |
|       CloseHandle (pi.hThread);
 | |
|       hchild = pi.hProcess;
 | |
| 
 | |
|       /* Protect the handle but name it similarly to the way it will
 | |
| 	 be called in subproc handling. */
 | |
|       ProtectHandle1 (hchild, childhProc);
 | |
| 
 | |
|       strace.write_childpid (pi.dwProcessId);
 | |
| 
 | |
|       /* Wait for subproc to initialize itself. */
 | |
|       if (!ch.sync (pi.dwProcessId, hchild, FORK_WAIT_TIMEOUT))
 | |
| 	{
 | |
| 	  if (!error ("forked process %u died unexpectedly, retry %d, exit code %y",
 | |
| 		      pi.dwProcessId, ch.retry, ch.exit_code))
 | |
| 	    continue;
 | |
| 	  this_errno = EAGAIN;
 | |
| 	  goto cleanup;
 | |
| 	}
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   /* Restore impersonation */
 | |
|   cygheap->user.reimpersonate ();
 | |
|   fix_impersonation = false;
 | |
| 
 | |
|   child_pid = cygwin_pid (pi.dwProcessId);
 | |
|   child.init (child_pid, PID_IN_USE | PID_NEW, NULL);
 | |
| 
 | |
|   if (!child)
 | |
|     {
 | |
|       this_errno = get_errno () == ENOMEM ? ENOMEM : EAGAIN;
 | |
|       syscall_printf ("pinfo failed");
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   child->nice = myself->nice;
 | |
| 
 | |
|   /* Initialize things that are done later in dll_crt0_1 that aren't done
 | |
|      for the forkee.  */
 | |
|   wcscpy (child->progname, myself->progname);
 | |
| 
 | |
|   /* Fill in fields in the child's process table entry.  */
 | |
|   child->dwProcessId = pi.dwProcessId;
 | |
|   child.hProcess = hchild;
 | |
|   ch.postfork (child);
 | |
| 
 | |
|   /* Hopefully, this will succeed.  The alternative to doing things this
 | |
|      way is to reserve space prior to calling CreateProcess and then fill
 | |
|      it in afterwards.  This requires more bookkeeping than I like, though,
 | |
|      so we'll just do it the easy way.  So, terminate any child process if
 | |
|      we can't actually record the pid in the internal table. */
 | |
|   if (!child.remember (false))
 | |
|     {
 | |
|       TerminateProcess (hchild, 1);
 | |
|       this_errno = EAGAIN;
 | |
| #ifdef DEBUGGING0
 | |
|       error ("child remember failed");
 | |
| #endif
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
| #ifndef NO_SLOW_PID_REUSE
 | |
|   slow_pid_reuse (hchild);
 | |
| #endif
 | |
| 
 | |
|   /* CHILD IS STOPPED */
 | |
|   debug_printf ("child is alive (but stopped)");
 | |
| 
 | |
| 
 | |
|   /* Initialize, in order: stack, dll data, dll bss.
 | |
|      data, bss, heap were done earlier (in dcrt0.cc)
 | |
|      Note: variables marked as NO_COPY will not be copied since they are
 | |
|      placed in a protected segment.  */
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
|   const void *impure_beg;
 | |
|   const void *impure_end;
 | |
|   const char *impure;
 | |
|   if (&_my_tls == _main_tls)
 | |
|     impure_beg = impure_end = impure = NULL;
 | |
|   else
 | |
|     {
 | |
|       impure = "impure";
 | |
|       impure_beg = _impure_ptr;
 | |
|       impure_end = _impure_ptr + 1;
 | |
|     }
 | |
|   rc = child_copy (hchild, true,
 | |
| 		   "stack", stack_here, ch.stackbottom,
 | |
| 		   impure, impure_beg, impure_end,
 | |
| 		   NULL);
 | |
| 
 | |
|   __malloc_unlock ();
 | |
|   locked = false;
 | |
|   MALLOC_CHECK;
 | |
|   if (!rc)
 | |
|     {
 | |
|       this_errno = get_errno ();
 | |
|       error ("pid %u, exitval %p", pi.dwProcessId, ch.exit_code);
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* Now fill data/bss of any DLLs that were linked into the program. */
 | |
|   for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
 | |
|     {
 | |
|       debug_printf ("copying data/bss of a linked dll");
 | |
|       if (!child_copy (hchild, true,
 | |
| 		       "linked dll data", d->p.data_start, d->p.data_end,
 | |
| 		       "linked dll bss", d->p.bss_start, d->p.bss_end,
 | |
| 		       NULL))
 | |
| 	{
 | |
| 	  this_errno = get_errno ();
 | |
| 	  error ("couldn't copy linked dll data/bss");
 | |
| 	  goto cleanup;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* Start thread, and then wait for it to reload dlls.  */
 | |
|   resume_child (forker_finished);
 | |
|   if (!ch.sync (child->pid, hchild, FORK_WAIT_TIMEOUT))
 | |
|     {
 | |
|       this_errno = EAGAIN;
 | |
|       error ("died waiting for dll loading");
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* If DLLs were loaded in the parent, then the child has reloaded all
 | |
|      of them and is now waiting to have all of the individual data and
 | |
|      bss sections filled in. */
 | |
|   if (load_dlls)
 | |
|     {
 | |
|       /* CHILD IS STOPPED */
 | |
|       /* write memory of reloaded dlls */
 | |
|       for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
 | |
| 	{
 | |
| 	  debug_printf ("copying data/bss for a loaded dll");
 | |
| 	  if (!child_copy (hchild, true,
 | |
| 			   "loaded dll data", d->p.data_start, d->p.data_end,
 | |
| 			   "loaded dll bss", d->p.bss_start, d->p.bss_end,
 | |
| 			   NULL))
 | |
| 	    {
 | |
| 	      this_errno = get_errno ();
 | |
| #ifdef DEBUGGING
 | |
| 	      error ("copying data/bss for a loaded dll");
 | |
| #endif
 | |
| 	      goto cleanup;
 | |
| 	    }
 | |
| 	}
 | |
|       /* Start the child up again. */
 | |
|       resume_child (forker_finished);
 | |
|     }
 | |
| 
 | |
|   ForceCloseHandle (forker_finished);
 | |
|   forker_finished = NULL;
 | |
| 
 | |
|   return child_pid;
 | |
| 
 | |
| /* Common cleanup code for failure cases */
 | |
| cleanup:
 | |
|   if (fix_impersonation)
 | |
|     cygheap->user.reimpersonate ();
 | |
|   if (locked)
 | |
|     __malloc_unlock ();
 | |
| 
 | |
|   /* Remember to de-allocate the fd table. */
 | |
|   if (hchild && !child.hProcess)
 | |
|     ForceCloseHandle1 (hchild, childhProc);
 | |
|   if (forker_finished)
 | |
|     ForceCloseHandle (forker_finished);
 | |
|   debug_printf ("returning -1");
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| fork ()
 | |
| {
 | |
|   frok grouped;
 | |
| 
 | |
|   debug_printf ("entering");
 | |
|   grouped.load_dlls = 0;
 | |
| 
 | |
|   int res;
 | |
|   bool ischild = false;
 | |
| 
 | |
|   myself->set_has_pgid_children ();
 | |
| 
 | |
|   if (grouped.ch.parent == NULL)
 | |
|     return -1;
 | |
|   if (grouped.ch.subproc_ready == NULL)
 | |
|     {
 | |
|       system_printf ("unable to allocate subproc_ready event, %E");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   {
 | |
|     hold_everything held_everything (ischild);
 | |
|     /* This tmp_pathbuf constructor is required here because the below setjmp
 | |
|        magic will otherwise not restore the original buffer count values in
 | |
|        the thread-local storage.  A process forking too deeply will run into
 | |
|        the problem to be out of temporary TLS path buffers. */
 | |
|     tmp_pathbuf tp;
 | |
| 
 | |
|     if (!held_everything)
 | |
|       {
 | |
| 	if (exit_state)
 | |
| 	  Sleep (INFINITE);
 | |
| 	set_errno (EAGAIN);
 | |
| 	return -1;
 | |
|       }
 | |
| 
 | |
|     /* Put the dll list in topological dependency ordering, in
 | |
|        hopes that the child will have a better shot at loading dlls
 | |
|        properly if it only has to deal with one at a time.  */
 | |
|     dlls.topsort ();
 | |
| 
 | |
|     ischild = !!setjmp (grouped.ch.jmp);
 | |
| 
 | |
|     volatile char * volatile stackp;
 | |
| #ifdef __x86_64__
 | |
|     __asm__ volatile ("movq %%rsp,%0": "=r" (stackp));
 | |
| #else
 | |
|     __asm__ volatile ("movl %%esp,%0": "=r" (stackp));
 | |
| #endif
 | |
| 
 | |
|     if (!ischild)
 | |
|       res = grouped.parent (stackp);
 | |
|     else
 | |
|       {
 | |
| 	res = grouped.child (stackp);
 | |
| 	in_forkee = false;
 | |
| 	ischild = true;	/* might have been reset by fork mem copy */
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   MALLOC_CHECK;
 | |
|   if (ischild)
 | |
|     {
 | |
|       myself->process_state |= PID_ACTIVE;
 | |
|       myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED);
 | |
|     }
 | |
|   else if (res < 0)
 | |
|     {
 | |
|       if (!grouped.errmsg)
 | |
| 	syscall_printf ("fork failed - child pid %d, errno %d", grouped.child_pid, grouped.this_errno);
 | |
|       else
 | |
| 	{
 | |
| 	  char buf[strlen (grouped.errmsg) + sizeof ("child %d - , errno 4294967295  ")];
 | |
| 	  strcpy (buf, "child %d - ");
 | |
| 	  strcat (buf, grouped.errmsg);
 | |
| 	  strcat (buf, ", errno %d");
 | |
| 	  system_printf (buf, grouped.child_pid, grouped.this_errno);
 | |
| 	}
 | |
| 
 | |
|       set_errno (grouped.this_errno);
 | |
|     }
 | |
|   syscall_printf ("%R = fork()", res);
 | |
|   return res;
 | |
| }
 | |
| #ifdef DEBUGGING
 | |
| void
 | |
| fork_init ()
 | |
| {
 | |
| }
 | |
| #endif /*DEBUGGING*/
 | |
| 
 | |
| 
 | |
| extern "C" int
 | |
| vfork ()
 | |
| {
 | |
|   debug_printf ("stub called");
 | |
|   return fork ();
 | |
| }
 | |
| 
 | |
| /* Copy memory from one process to another. */
 | |
| 
 | |
| bool
 | |
| child_copy (HANDLE hp, bool write, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   va_start (args, write);
 | |
|   static const char *huh[] = {"read", "write"};
 | |
| 
 | |
|   char *what;
 | |
|   while ((what = va_arg (args, char *)))
 | |
|     {
 | |
|       char *low = va_arg (args, char *);
 | |
|       char *high = va_arg (args, char *);
 | |
|       SIZE_T todo = high - low;
 | |
|       char *here;
 | |
| 
 | |
|       for (here = low; here < high; here += todo)
 | |
| 	{
 | |
| 	  SIZE_T done = 0;
 | |
| 	  if (here + todo > high)
 | |
| 	    todo = high - here;
 | |
| 	  int res;
 | |
| 	  if (write)
 | |
| 	    res = WriteProcessMemory (hp, here, here, todo, &done);
 | |
| 	  else
 | |
| 	    res = ReadProcessMemory (hp, here, here, todo, &done);
 | |
| 	  debug_printf ("%s - hp %p low %p, high %p, res %d", what, hp, low, high, res);
 | |
| 	  if (!res || todo != done)
 | |
| 	    {
 | |
| 	      if (!res)
 | |
| 		__seterrno ();
 | |
| 	      /* If this happens then there is a bug in our fork
 | |
| 		 implementation somewhere. */
 | |
| 	      system_printf ("%s %s copy failed, %p..%p, done %lu, windows pid %u, %E",
 | |
| 			    what, huh[write], low, high, done, myself->dwProcessId);
 | |
| 	      goto err;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   va_end (args);
 | |
|   debug_printf ("done");
 | |
|   return true;
 | |
| 
 | |
|  err:
 | |
|   va_end (args);
 | |
|   TerminateProcess (hp, 1);
 | |
|   set_errno (EAGAIN);
 | |
|   return false;
 | |
| }
 |