/* fork.cc
- Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005
- Red Hat, Inc.
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
+ 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
This file is part of Cygwin.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
-#include <stdarg.h>
#include "cygerrno.h"
-#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "child_info.h"
#include "cygtls.h"
-#include "perprocess.h"
+#include "tls_pbuf.h"
#include "dll_init.h"
-#include "sync.h"
-#include "shared_info.h"
#include "cygmalloc.h"
-#include "cygthread.h"
+#include "ntdll.h"
#define NPIDS_HELD 4
/* FIXME: Once things stabilize, bump up to a few minutes. */
#define FORK_WAIT_TIMEOUT (300 * 1000) /* 300 seconds */
-#define dll_data_start &_data_start__
-#define dll_data_end &_data_end__
-#define dll_bss_start &_bss_start__
-#define dll_bss_end &_bss_end__
-
-static void
-stack_base (child_info_fork &ch)
+class frok
{
- MEMORY_BASIC_INFORMATION m;
- memset (&m, 0, sizeof m);
- if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m))
- system_printf ("couldn't get memory info, %E");
-
- ch.stacktop = m.AllocationBase;
- ch.stackbottom = (LPBYTE) m.BaseAddress + m.RegionSize;
- ch.stacksize = (DWORD) ch.stackbottom - (DWORD) &m;
- debug_printf ("bottom %p, top %p, stack %p, size %d, reserve %d",
- ch.stackbottom, ch.stacktop, &m, ch.stacksize,
- (DWORD) ch.stackbottom - (DWORD) ch.stacktop);
-}
+ 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 ();
+};
+
+class lock_signals
+{
+ bool worked;
+public:
+ lock_signals ()
+ {
+ worked = sig_send (NULL, __SIGHOLD) == 0;
+ }
+ operator int () const
+ {
+ return worked;
+ }
+ void dont_bother ()
+ {
+ worked = false;
+ }
+ ~lock_signals ()
+ {
+ if (worked)
+ sig_send (NULL, __SIGNOHOLD);
+ }
+};
-/* Copy memory from parent to child.
- The result is a boolean indicating success. */
+class lock_pthread
+{
+ bool bother;
+public:
+ lock_pthread (): bother (1)
+ {
+ pthread::atforkprepare ();
+ }
+ void dont_bother ()
+ {
+ bother = false;
+ }
+ ~lock_pthread ()
+ {
+ if (bother)
+ pthread::atforkparent ();
+ }
+};
-static int
-fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
+class hold_everything
{
- va_list args;
- char *low;
- int pass = 0;
+public: /* DELETEME*/
+ bool& ischild;
+ /* Note the order of the locks below. It is important,
+ to avoid races, that the lock order be preserved.
- va_start (args, what);
+ pthread is first because it serves as a master lock
+ against other forks being attempted while this one is active.
- while ((low = va_arg (args, char *)))
- {
- char *high = va_arg (args, char *);
- DWORD todo = wincap.chunksize () ?: high - low;
- char *here;
+ signals is next to stop signal processing for the duration
+ of the fork.
- for (here = low; here < high; here += todo)
- {
- DWORD done = 0;
- if (here + todo > high)
- todo = high - here;
- int res;
- if (pi.hThread)
- res = WriteProcessMemory (pi.hProcess, here, here, todo, &done);
- else
- res = ReadProcessMemory (pi.hProcess, here, here, todo, &done);
- debug_printf ("child handle %p, low %p, high %p, res %d", pi.hProcess,
- 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 pass %d failed, %p..%p, done %d, windows pid %u, %E",
- what, pass, low, high, done, pi.dwProcessId);
- goto err;
- }
- }
+ process is last. If it is put before signals, then a deadlock
+ could be introduced if the process attempts to exit due to a signal. */
+ lock_pthread pthread;
+ lock_signals signals;
+ lock_process process;
- pass++;
- }
+public:
+ hold_everything (bool& x): ischild (x) {}
+ operator int () const {return signals;}
- debug_printf ("done");
- return 1;
+ ~hold_everything()
+ {
+ if (ischild)
+ {
+ pthread.dont_bother ();
+ process.dont_bother ();
+ signals.dont_bother ();
+ }
+ }
- err:
- TerminateProcess (pi.hProcess, 1);
- set_errno (EAGAIN);
- return 0;
-}
+};
-static int
+static void
resume_child (HANDLE forker_finished)
{
SetEvent (forker_finished);
debug_printf ("signalled child");
- return 1;
+ return;
}
/* Notify parent that it is time for the next step. */
}
}
-static int __stdcall
-fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
+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;
+}
+
+int __stdcall
+frok::child (volatile char * volatile here)
+{
+ HANDLE& hParent = ch.parent;
+ extern void fixup_lockf_after_fork ();
extern void fixup_hooks_after_fork ();
extern void fixup_timers_after_fork ();
debug_printf ("child is running. pid %d, ppid %d, stack here %p",
myself->pid, myself->ppid, __builtin_frame_address (0));
+ /* 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);
- sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
- first_dll, load_dlls);
+ 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->stacksize)
+ if (fork_info->stackaddr)
{
_main_tls = &_my_tls;
_main_tls->init_thread (NULL, NULL);
_impure_ptr = &_main_tls->local_clib;
}
- if (wincap.has_security ())
- {
- set_cygwin_privileges (hProcImpToken);
- cygheap->user.reimpersonate ();
- }
+ set_cygwin_privileges (hProcToken);
+ clear_procimptoken ();
+ cygheap->user.reimpersonate ();
#ifdef DEBUGGING
- char c;
- if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+ 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 (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
+ if (GetEnvironmentVariableA ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
{
small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
Sleep (atoi (buf));
}
#endif
- set_file_api_mode (current_codepage);
-
MALLOC_CHECK;
- if (fixup_mmaps_after_fork (hParent))
- api_fatal ("recreate_mmaps_after_fork_failed");
-
-#ifdef USE_SERVER
/* 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
fdtab before fixing up shared memory. */
if (fixup_shms_after_fork ())
api_fatal ("recreate_shm areas after fork failed");
-#endif
MALLOC_CHECK;
}
else
{
- dlls.load_after_fork (hParent, first_dll);
+ 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);
- _my_tls.fixup_after_fork ();
- sigproc_init ();
-
pthread::atforkchild ();
fixup_timers_after_fork ();
- fixup_hooks_after_fork ();
cygbench ("fork-child");
+ ld_preload ();
+ fixup_hooks_after_fork ();
+ _my_tls.fixup_after_fork ();
cygwin_finished_initializing = true;
return 0;
}
+#define NO_SLOW_PID_REUSE
#ifndef NO_SLOW_PID_REUSE
static void
slow_pid_reuse (HANDLE h)
windows from reusing the same pid. */
if (last_fork_procs[nfork_procs])
ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
- if (DuplicateHandle (hMainProc, h, hMainProc, &last_fork_procs[nfork_procs],
- 0, FALSE, DUPLICATE_SAME_ACCESS))
+ if (DuplicateHandle (GetCurrentProcess (), h,
+ GetCurrentProcess (), &last_fork_procs[nfork_procs],
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
else
{
}
#endif
-static int __stdcall
-fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_info_fork &ch)
+int __stdcall
+frok::parent (volatile char * volatile stack_here)
{
HANDLE forker_finished;
DWORD rc;
- PROCESS_INFORMATION pi = {0, NULL, 0, 0};
+ child_pid = -1;
+ this_errno = 0;
+ bool fix_impersonation = false;
+ pinfo child;
- pthread::atforkprepare ();
+ int c_flags = GetPriorityClass (GetCurrentProcess ());
+ debug_printf ("priority class %d", c_flags);
- int c_flags = GetPriorityClass (hMainProc) /*|
- CREATE_NEW_PROCESS_GROUP*/;
- STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+ errmsg = NULL;
+ hchild = NULL;
/* If we don't have a console, then don't create a console for the
child either. */
if (cygheap->fdtab.need_fixup_before ())
c_flags |= CREATE_SUSPENDED;
- /* Remember the address of the first loaded dll and decide
- if we need to load dlls. We do this here so that this
- information will be available in the parent and, when
- the stack is copied, in the child. */
- first_dll = dlls.start.next;
+ /* 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;
- /* This will help some of the confusion. */
- fflush (stdout);
-
forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
if (forker_finished == NULL)
{
- system_printf ("unable to allocate forker_finished event, %E");
+ this_errno = geterrno_from_win_error ();
+ error ("unable to allocate forker_finished event");
return -1;
}
ch.forker_finished = forker_finished;
- stack_base (ch);
+ ch.stackbottom = _tlsbase;
+ ch.stacktop = (void *) _tlstop;
+ ch.stackaddr = 0;
+ 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. */
+ PTEB teb = NtCurrentTeb ();
+ if (!teb->DeallocationStack)
+ {
+ /* 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
+ {
+ ch.stackaddr = teb->DeallocationStack;
+ /* If it's a pthread, fetch guardsize from thread attributes. */
+ if (_my_tls.tid)
+ ch.guardsize = _my_tls.tid->attr.guardsize;
+ }
+ }
+ debug_printf ("stack - bottom %p, top %p, addr %p, guardsize %p",
+ ch.stackbottom, ch.stacktop, ch.stackaddr, ch.guardsize);
+
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+
+ memset (&si, 0, sizeof (si));
+ si.cb = sizeof si;
- si.cb = sizeof (STARTUPINFO);
si.lpReserved2 = (LPBYTE) &ch;
si.cbReserved2 = sizeof (ch);
- /* Remove impersonation */
- cygheap->user.deimpersonate ();
-
- syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %x, 0, 0, %p, %p)",
+ syscall_printf ("CreateProcessW (%W, %W, 0, 0, 1, %p, 0, 0, %p, %p)",
myself->progname, myself->progname, c_flags, &si, &pi);
bool locked = __malloc_lock ();
- rc = CreateProcess (myself->progname, /* image to run */
- myself->progname, /* what we send in arg0 */
- &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);
+ time_t start_time = time (NULL);
- if (!rc)
- {
- __seterrno ();
- syscall_printf ("CreateProcessA failed, %E");
- ForceCloseHandle (forker_finished);
- /* Restore impersonation */
- cygheap->user.reimpersonate ();
- __malloc_unlock ();
- return -1;
- }
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+ fix_impersonation = true;
+ ch.refresh_cygheap ();
- /* Fixup the parent datastructure if needed and resume the child's
- main thread. */
- if (cygheap->fdtab.need_fixup_before ())
+ while (1)
{
- cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
- ResumeThread (pi.hThread);
+ hchild = NULL;
+ rc = CreateProcessW (myself->progname, /* image to run */
+ myself->progname, /* what we send in arg0 */
+ &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)
+ {
+ 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 (ch, pi.dwProcessId);
+
+ /* Wait for subproc to initialize itself. */
+ if (!ch.sync (pi.dwProcessId, hchild, FORK_WAIT_TIMEOUT))
+ {
+ if (!error ("forked process died unexpectedly, retry %d, exit code %d",
+ ch.retry, ch.exit_code))
+ continue;
+ this_errno = EAGAIN;
+ goto cleanup;
+ }
+ break;
}
- int child_pid = cygwin_pid (pi.dwProcessId);
- pinfo child (child_pid, 1);
- child->start_time = time (NULL); /* Register child's starting time. */
- child->nice = myself->nice;
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+ fix_impersonation = false;
+
+ child_pid = cygwin_pid (pi.dwProcessId);
+ child.init (child_pid, 1, NULL);
if (!child)
{
+ this_errno = get_errno () == ENOMEM ? ENOMEM : EAGAIN;
syscall_printf ("pinfo failed");
- if (get_errno () != ENOMEM)
- set_errno (EAGAIN);
goto cleanup;
}
+ child->start_time = start_time; /* Register child's starting time. */
+ child->nice = myself->nice;
+
/* Initialize things that are done later in dll_crt0_1 that aren't done
for the forkee. */
- strcpy (child->progname, myself->progname);
-
- /* Restore impersonation */
- cygheap->user.reimpersonate ();
-
- ProtectHandle (pi.hThread);
- /* Protect the handle but name it similarly to the way it will
- be called in subproc handling. */
- ProtectHandle1 (pi.hProcess, childhProc);
+ wcscpy (child->progname, myself->progname);
/* Fill in fields in the child's process table entry. */
child->dwProcessId = pi.dwProcessId;
- child.hProcess = pi.hProcess;
+ child.hProcess = hchild;
/* Hopefully, this will succeed. The alternative to doing things this
way is to reserve space prior to calling CreateProcess and then fill
we can't actually record the pid in the internal table. */
if (!child.remember (false))
{
- TerminateProcess (pi.hProcess, 1);
- set_errno (EAGAIN);
+ TerminateProcess (hchild, 1);
+ this_errno = EAGAIN;
+#ifdef DEBUGGING0
+ error ("child remember failed");
+#endif
goto cleanup;
}
#ifndef NO_SLOW_PID_REUSE
- slow_pid_reuse (pi.hProcess);
+ slow_pid_reuse (hchild);
#endif
- /* Wait for subproc to initialize itself. */
- if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
- {
- system_printf ("child %d died waiting for longjmp before initialization", child_pid);
- goto cleanup;
- }
-
/* CHILD IS STOPPED */
debug_printf ("child is alive (but stopped)");
- /* Initialize, in order: data, bss, heap, stack, dll data, dll bss
- Note: variables marked as NO_COPY will not be copied
- since they are placed in a protected segment. */
-
+ /* 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;
- void *impure_beg;
- void *impure_end;
+ const void *impure_beg;
+ const void *impure_end;
+ const char *impure;
if (&_my_tls == _main_tls)
- impure_beg = impure_end = NULL;
+ impure_beg = impure_end = impure = NULL;
else
{
+ impure = "impure";
impure_beg = _impure_ptr;
impure_end = _impure_ptr + 1;
}
- rc = fork_copy (pi, "user/cygwin data",
- user_data->data_start, user_data->data_end,
- user_data->bss_start, user_data->bss_end,
- cygheap->user_heap.base, cygheap->user_heap.ptr,
- stack_here, ch.stackbottom,
- dll_data_start, dll_data_end,
- dll_bss_start, dll_bss_end, impure_beg, impure_end, NULL);
+ rc = child_copy (hchild, true,
+ "stack", stack_here, ch.stackbottom,
+ impure, impure_beg, impure_end,
+ NULL);
__malloc_unlock ();
locked = false;
MALLOC_CHECK;
if (!rc)
- goto cleanup;
+ {
+ 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 (!fork_copy (pi, "linked dll data/bss", d->p.data_start, d->p.data_end,
- d->p.bss_start, d->p.bss_end,
- NULL))
- goto cleanup;
+ 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 wait for it to reload dlls. */
- if (!resume_child (forker_finished))
- goto cleanup;
- else if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
+ /* Start thread, and then wait for it to reload dlls. */
+ resume_child (forker_finished);
+ if (!ch.sync (child->pid, hchild, FORK_WAIT_TIMEOUT))
{
- system_printf ("child %d died waiting for dll loading", child_pid);
+ this_errno = EAGAIN;
+ error ("died waiting for dll loading");
goto cleanup;
}
for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
{
debug_printf ("copying data/bss for a loaded dll");
- if (!fork_copy (pi, "loaded dll data/bss", d->p.data_start, d->p.data_end,
- d->p.bss_start, d->p.bss_end,
- NULL))
- goto cleanup;
+ 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 (pi.hThread);
ForceCloseHandle (forker_finished);
forker_finished = NULL;
- pi.hThread = NULL;
- pthread::atforkparent ();
return child_pid;
/* Common cleanup code for failure cases */
- cleanup:
+cleanup:
+ if (fix_impersonation)
+ cygheap->user.reimpersonate ();
if (locked)
__malloc_unlock ();
/* Remember to de-allocate the fd table. */
- if (pi.hProcess)
- ForceCloseHandle1 (pi.hProcess, childhProc);
- if (pi.hThread)
- ForceCloseHandle (pi.hThread);
+ if (hchild && !child.hProcess)
+ ForceCloseHandle1 (hchild, childhProc);
if (forker_finished)
ForceCloseHandle (forker_finished);
+ debug_printf ("returning -1");
return -1;
}
extern "C" int
fork ()
{
- struct
- {
- dll *first_dll;
- bool load_dlls;
- child_info_fork ch;
- } grouped;
-
- MALLOC_CHECK;
+ frok grouped;
debug_printf ("entering");
- grouped.first_dll = NULL;
grouped.load_dlls = 0;
- void *esp;
- __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+ int res;
+ bool ischild = false;
myself->set_has_pgid_children ();
return -1;
}
- sig_send (NULL, __SIGHOLD);
- int res = setjmp (grouped.ch.jmp);
- if (res)
- res = fork_child (grouped.ch.parent, grouped.first_dll, grouped.load_dlls);
- else
- res = fork_parent (grouped.ch.parent, grouped.first_dll, grouped.load_dlls, esp, grouped.ch);
- sig_send (NULL, __SIGNOHOLD);
+ {
+ 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 esp;
+ __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+ if (!ischild)
+ res = grouped.parent (esp);
+ else
+ {
+ res = grouped.child (esp);
+ 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 ("%d = fork()", res);
return res;
}
}
#endif /*DEBUGGING*/
-#ifdef NEWVFORK
-/* Dummy function to force second assignment below to actually be
- carried out */
-static vfork_save *
-get_vfork_val ()
-{
- return vfork_storage.val ();
-}
-#endif
extern "C" int
vfork ()
{
-#ifndef NEWVFORK
debug_printf ("stub called");
return fork ();
-#else
- vfork_save *vf = get_vfork_val ();
- char **esp, **pp;
-
- if (vf == NULL)
- vf = vfork_storage.create ();
- else if (vf->pid)
- return fork ();
-
- // FIXME the tls stuff could introduce a signal race if a child process
- // exits quickly.
- if (!setjmp (vf->j))
- {
- vf->pid = -1;
- __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
- __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
- for (pp = (char **) vf->frame, esp = vf->vfork_esp;
- esp <= vf->vfork_ebp + 2; pp++, esp++)
- *pp = *esp;
- vf->ctty = myself->ctty;
- vf->sid = myself->sid;
- vf->pgid = myself->pgid;
- cygheap->ctty_on_hold = cygheap->ctty;
- vf->open_fhs = cygheap->open_fhs;
- debug_printf ("cygheap->ctty_on_hold %p, cygheap->open_fhs %d", cygheap->ctty_on_hold, cygheap->open_fhs);
- int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
- debug_printf ("%d = vfork()", res);
- _my_tls.call_signal_handler (); // FIXME: racy
- vf->tls = _my_tls;
- return res;
- }
-
- vf = get_vfork_val ();
-
- for (pp = (char **) vf->frame, esp = vf->vfork_esp;
- esp <= vf->vfork_ebp + 2; pp++, esp++)
- *esp = *pp;
+}
- cygheap->fdtab.vfork_parent_restore ();
+/* Copy memory from one process to another. */
- myself->ctty = vf->ctty;
- myself->sid = vf->sid;
- myself->pgid = vf->pgid;
- termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
- cygheap->open_fhs = vf->open_fhs;
+bool
+child_copy (HANDLE hp, bool write, ...)
+{
+ va_list args;
+ va_start (args, write);
+ static const char *huh[] = {"read", "write"};
- if (vf->pid < 0)
+ char *what;
+ while ((what = va_arg (args, char *)))
{
- int exitval = vf->exitval;
- vf->pid = 0;
- if ((vf->pid = fork ()) == 0)
- exit (exitval);
- }
+ char *low = va_arg (args, char *);
+ char *high = va_arg (args, char *);
+ DWORD todo = high - low;
+ char *here;
- int pid = vf->pid;
- vf->pid = 0;
- debug_printf ("exiting vfork, pid %d", pid);
- sig_dispatch_pending ();
+ for (here = low; here < high; here += todo)
+ {
+ DWORD 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 %d, windows pid %u, %E",
+ what, huh[write], low, high, done, myself->dwProcessId);
+ goto err;
+ }
+ }
+ }
- _my_tls.call_signal_handler (); // FIXME: racy
- _my_tls = vf->tls;
- return pid;
-#endif
-}
+ va_end (args);
+ debug_printf ("done");
+ return true;
-int
-child_copy (HANDLE h, DWORD pid, const char *what, void *child_start, void *child_end)
-{
- PROCESS_INFORMATION pi;
- pi.hProcess = h;
- pi.dwProcessId = pid;
- pi.hThread = NULL;
- debug_printf ("%s, start %p, end %p", what, child_start, child_end);
- return fork_copy (pi, what, child_start, child_end, NULL);
+ err:
+ va_end (args);
+ TerminateProcess (hp, 1);
+ set_errno (EAGAIN);
+ return false;
}