summaryrefslogtreecommitdiff
path: root/winsup/cygwin/fork.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fork.cc')
-rw-r--r--winsup/cygwin/fork.cc751
1 files changed, 751 insertions, 0 deletions
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
new file mode 100644
index 00000000000..7639a621d47
--- /dev/null
+++ b/winsup/cygwin/fork.cc
@@ -0,0 +1,751 @@
+/* fork.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 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 <stdarg.h>
+#include <errno.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "child_info.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "perprocess.h"
+#include "dll_init.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "cygmalloc.h"
+#include "cygthread.h"
+
+#ifdef DEBUGGING
+static int npid;
+static int npid_max;
+static pid_t fork_pids[100];
+#endif
+
+/* 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 */
+
+#define dll_data_start &_data_start__
+#define dll_data_end &_data_end__
+#define dll_bss_start &_bss_start__
+#define dll_bss_end &_bss_end__
+
+void
+per_thread::set (void *s)
+{
+ if (s == PER_THREAD_FORK_CLEAR)
+ {
+ tls = TlsAlloc ();
+ s = NULL;
+ }
+ TlsSetValue (get_tls (), s);
+}
+
+static void
+stack_base (child_info_fork &ch)
+{
+ 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);
+}
+
+/* Copy memory from parent to child.
+ The result is a boolean indicating success. */
+
+static int
+fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
+{
+ va_list args;
+ char *low;
+ int pass = 0;
+
+ va_start (args, what);
+
+ while ((low = va_arg (args, char *)))
+ {
+ char *high = va_arg (args, char *);
+ DWORD todo = wincap.chunksize () ?: high - low;
+ char *here;
+
+ for (here = low; here < high; here += todo)
+ {
+ DWORD done = 0;
+ if (here + todo > high)
+ todo = high - here;
+ int res = WriteProcessMemory (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;
+ }
+ }
+
+ pass++;
+ }
+
+ debug_printf ("done");
+ return 1;
+
+ err:
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ return 0;
+}
+
+/* Wait for child to finish what it's doing and signal us.
+ We don't want to wait forever here.If there's a problem somewhere
+ it'll hang the entire system (since all forks are mutex'd). If we
+ time out, set errno = EAGAIN and hope the app tries again. */
+static int
+sync_with_child (PROCESS_INFORMATION &pi, HANDLE subproc_ready,
+ BOOL hang_child, const char *s)
+{
+ /* We also add the child process handle to the wait. If the child fails
+ to initialize (eg. because of a missing dll). Then this
+ handle will become signalled. This stops a *looong* timeout wait.
+ */
+ HANDLE w4[2];
+
+ debug_printf ("waiting for child. reason: %s, hang_child %d", s,
+ hang_child);
+ w4[1] = pi.hProcess;
+ w4[0] = subproc_ready;
+ DWORD rc = WaitForMultipleObjects (2, w4, FALSE, FORK_WAIT_TIMEOUT);
+
+ if (rc == WAIT_OBJECT_0 ||
+ WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0)
+ /* That's ok */;
+ else if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT)
+ {
+ if (rc != WAIT_FAILED)
+ system_printf ("WaitForMultipleObjects timed out");
+ else
+ system_printf ("WaitForMultipleObjects failed, %E");
+ set_errno (EAGAIN);
+ syscall_printf ("-1 = fork(), WaitForMultipleObjects failed");
+ TerminateProcess (pi.hProcess, 1);
+ return 0;
+ }
+ else
+ {
+ /* Child died. Clean up and exit. */
+ DWORD errcode;
+ GetExitCodeProcess (pi.hProcess, &errcode);
+ /* Fix me. This is not enough. The fork should not be considered
+ * to have failed if the process was essentially killed by a signal.
+ */
+ if (errcode != STATUS_CONTROL_C_EXIT)
+ {
+ system_printf ("child %d(%p) died before initialization with status code %p",
+ pi.dwProcessId, pi.hProcess, errcode);
+ system_printf ("*** child state %s", s);
+#ifdef DEBUGGING
+ abort ();
+#endif
+ }
+ set_errno (EAGAIN);
+ syscall_printf ("Child died before subproc_ready signalled");
+ return 0;
+ }
+
+ debug_printf ("child signalled me");
+ return 1;
+}
+
+static int
+resume_child (PROCESS_INFORMATION &pi, HANDLE forker_finished)
+{
+ SetEvent (forker_finished);
+ debug_printf ("signalled child");
+ return 1;
+}
+
+/* Notify parent that it is time for the next step.
+ Note that this has to be a macro since the parent may be messing with
+ our stack. */
+static void __stdcall
+sync_with_parent (const char *s, bool hang_self)
+{
+ debug_printf ("signalling parent: %s", s);
+ /* Tell our parent we're waiting. */
+ if (!SetEvent (fork_info->subproc_ready))
+ api_fatal ("fork child - SetEvent for %s failed, %E", s);
+ 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 for %s", s);
+ break;
+ case WAIT_FAILED:
+ if (GetLastError () == ERROR_INVALID_HANDLE &&
+ WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
+ break;
+ api_fatal ("WFSO failed for %s, fork_finished %p, %E", s,
+ fork_info->forker_finished);
+ break;
+ default:
+ debug_printf ("no problems");
+ break;
+ }
+ }
+}
+
+static int __stdcall
+fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
+{
+ debug_printf ("child is running. pid %d, ppid %d, stack here %p",
+ myself->pid, myself->ppid, __builtin_frame_address (0));
+
+ /* Restore the inheritance state as in parent
+ Don't call setuid here! The flags are already set. */
+ if (cygheap->user.impersonated)
+ {
+ debug_printf ("Impersonation of child, token: %d", cygheap->user.token);
+ if (cygheap->user.token == INVALID_HANDLE_VALUE)
+ RevertToSelf (); // probably not needed
+ else if (!ImpersonateLoggedOnUser (cygheap->user.token))
+ system_printf ("Impersonate for forked child failed: %E");
+ }
+
+ sync_with_parent ("after longjmp.", TRUE);
+ sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
+ first_dll, load_dlls);
+
+#ifdef DEBUGGING
+ char c;
+ if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+ 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)))
+ {
+ small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
+ Sleep (atoi (buf));
+ }
+#endif
+
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ when the "main" thread exits it calls do_exit, like a normal process.
+ Exit with a status code of 0. */
+ if (fork_info->stacksize)
+ {
+ ((DWORD *)fork_info->stackbottom)[-17] = (DWORD)do_exit;
+ ((DWORD *)fork_info->stackbottom)[-15] = (DWORD)0;
+ }
+
+ set_file_api_mode (current_codepage);
+
+ MALLOC_CHECK;
+
+ if (fixup_mmaps_after_fork (hParent))
+ api_fatal ("recreate_mmaps_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);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("performed fork fixup.", FALSE);
+ }
+ else
+ {
+ dlls.load_after_fork (hParent, first_dll);
+ cygheap->fdtab.fixup_after_fork (hParent);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("loaded dlls", TRUE);
+ }
+
+ ForceCloseHandle (hParent);
+ (void) ForceCloseHandle1 (fork_info->subproc_ready, subproc_ready);
+ (void) ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
+
+ if (fixup_shms_after_fork ())
+ api_fatal ("recreate_shm areas after fork failed");
+
+ pinfo_fixup_after_fork ();
+ signal_fixup_after_fork ();
+
+ /* Set thread local stuff to zero. Under Windows 95/98 this is sometimes
+ non-zero, for some reason.
+ FIXME: There is a memory leak here after a fork. */
+ for (per_thread **t = threadstuff; *t; t++)
+ if ((*t)->clear_on_fork ())
+ (*t)->set ();
+
+ pthread::atforkchild ();
+ wait_for_sigthread ();
+ cygbench ("fork-child");
+ return 0;
+}
+
+static void
+slow_pid_reuse (HANDLE h)
+{
+ static NO_COPY HANDLE last_fork_procs[8] = {0};
+ static NO_COPY unsigned nfork_procs = 0;
+
+ if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0])))
+ nfork_procs = 0;
+ /* Keep a list of handles to forked 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 (hMainProc, h, hMainProc, &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++;
+}
+
+static int __stdcall
+fork_parent (HANDLE& hParent, dll *&first_dll,
+ bool& load_dlls, void *stack_here, child_info_fork &ch)
+{
+ HANDLE subproc_ready, forker_finished;
+ DWORD rc;
+ PROCESS_INFORMATION pi = {0, NULL, 0, 0};
+
+ pthread::atforkprepare ();
+
+ subproc_init ();
+
+ 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};
+
+ /* 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_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;
+
+ /* Create an inheritable handle to pass to the child process. This will
+ allow the child to duplicate handles from the parent to itself. */
+ hParent = NULL;
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hParent, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("couldn't create handle to myself for child, %E");
+ return -1;
+ }
+
+ /* 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;
+ load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
+
+ /* This will help some of the confusion. */
+ fflush (stdout);
+
+ subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL);
+ if (subproc_ready == NULL)
+ {
+ CloseHandle (hParent);
+ system_printf ("unable to allocate subproc_ready event, %E");
+ return -1;
+ }
+ forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
+ if (forker_finished == NULL)
+ {
+ CloseHandle (hParent);
+ CloseHandle (subproc_ready);
+ system_printf ("unable to allocate forker_finished event, %E");
+ return -1;
+ }
+
+ ProtectHandleINH (subproc_ready);
+ ProtectHandleINH (forker_finished);
+
+ init_child_info (PROC_FORK, &ch, 1, subproc_ready);
+
+ ch.forker_finished = forker_finished;
+
+ stack_base (ch);
+
+ si.cb = sizeof (STARTUPINFO);
+ si.lpReserved2 = (LPBYTE)&ch;
+ si.cbReserved2 = sizeof (ch);
+
+ /* Remove impersonation */
+ if (cygheap->user.issetuid ())
+ RevertToSelf ();
+
+ ch.parent = hParent;
+#ifdef DEBUGGING
+ if (npid_max)
+ {
+ for (int pass = 0; pass < 2; pass++)
+ {
+ pid_t pid;
+ while ((pid = fork_pids[npid++]))
+ if (!pinfo (pid))
+ {
+ ch.cygpid = pid;
+ goto out;
+ }
+ npid = 0;
+ }
+ }
+ out:
+#endif
+
+ char sa_buf[1024];
+ PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf);
+ syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %x, 0, 0, %p, %p)",
+ myself->progname, myself->progname, c_flags, &si, &pi);
+ __malloc_lock ();
+ void *newheap;
+ newheap = cygheap_setup_for_child (&ch, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcess (myself->progname, /* image to run */
+ myself->progname, /* what we send in arg0 */
+ sec_attribs,
+ sec_attribs,
+ TRUE, /* inherit handles from parent */
+ c_flags,
+ NULL, /* environment filled in later */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+
+ CloseHandle (hParent);
+
+ if (!rc)
+ {
+ __seterrno ();
+ syscall_printf ("CreateProcessA failed, %E");
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (forker_finished);
+ /* Restore impersonation */
+ if (cygheap->user.issetuid ())
+ ImpersonateLoggedOnUser (cygheap->user.token);
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ return -1;
+ }
+
+ /* Fixup the parent datastructure if needed and resume the child's
+ main thread. */
+ if (!cygheap->fdtab.need_fixup_before ())
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ else
+ {
+ cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
+ cygheap_setup_for_child_cleanup (newheap, &ch, 1);
+ ResumeThread (pi.hThread);
+ }
+
+#ifdef DEBUGGING
+ pinfo forked ((ch.cygpid != 1 ? ch.cygpid : cygwin_pid (pi.dwProcessId)), 1);
+#else
+ pinfo forked (cygwin_pid (pi.dwProcessId), 1);
+#endif
+
+ /* Initialize things that are done later in dll_crt0_1 that aren't done
+ for the forkee. */
+ strcpy (forked->progname, myself->progname);
+
+ /* Restore impersonation */
+ if (cygheap->user.issetuid ())
+ ImpersonateLoggedOnUser (cygheap->user.token);
+
+ ProtectHandle (pi.hThread);
+ /* Protect the handle but name it similarly to the way it will
+ be called in subproc handling. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+
+ /* Fill in fields in the child's process table entry. */
+ forked->hProcess = pi.hProcess;
+ forked->dwProcessId = pi.dwProcessId;
+ forked->copysigs (myself);
+
+ /* 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 (!forked.remember ())
+ {
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ goto cleanup;
+ }
+
+ slow_pid_reuse (pi.hProcess);
+
+ /* Wait for subproc to initialize itself. */
+ if (!sync_with_child (pi, subproc_ready, TRUE, "waiting for longjmp"))
+ 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. */
+
+
+ MALLOC_CHECK;
+ 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, NULL);
+
+ __malloc_unlock ();
+ MALLOC_CHECK;
+ if (!rc)
+ 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;
+ }
+
+ /* Start thread, and wait for it to reload dlls. */
+ if (!resume_child (pi, forker_finished) ||
+ !sync_with_child (pi, subproc_ready, load_dlls, "child loading dlls"))
+ 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 (!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;
+ }
+ /* Start the child up again. */
+ (void) resume_child (pi, forker_finished);
+ }
+
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (pi.hThread);
+ ForceCloseHandle (forker_finished);
+ forker_finished = NULL;
+ pi.hThread = NULL;
+ pthread::atforkparent ();
+
+ return forked->pid;
+
+/* Common cleanup code for failure cases */
+ cleanup:
+ /* Remember to de-allocate the fd table. */
+ if (pi.hProcess)
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ if (pi.hThread)
+ ForceCloseHandle (pi.hThread);
+ if (subproc_ready)
+ ForceCloseHandle (subproc_ready);
+ if (forker_finished)
+ ForceCloseHandle (forker_finished);
+ return -1;
+}
+
+extern "C" int
+fork ()
+{
+ struct
+ {
+ HANDLE hParent;
+ dll *first_dll;
+ bool load_dlls;
+ } grouped;
+
+ MALLOC_CHECK;
+ sigframe thisframe (mainthread);
+
+ debug_printf ("entering");
+ grouped.hParent = grouped.first_dll = NULL;
+ grouped.load_dlls = 0;
+
+ if (ISSTATE(myself, PID_SPLIT_HEAP))
+ {
+ system_printf ("The heap has been split, CYGWIN can't fork this process.");
+ system_printf ("Increase the heap_chunk_size in the registry and try again.");
+ set_errno (ENOMEM);
+ syscall_printf ("-1 = fork (), split heap");
+ return -1;
+ }
+
+ void *esp;
+ __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+ myself->set_has_pgid_children ();
+
+ child_info_fork ch;
+
+ int res = setjmp (ch.jmp);
+
+ if (res)
+ res = fork_child (grouped.hParent, grouped.first_dll, grouped.load_dlls);
+ else
+ res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, esp, ch);
+
+ MALLOC_CHECK;
+ syscall_printf ("%d = fork()", res);
+ return res;
+}
+#ifdef DEBUGGING
+void
+fork_init ()
+{
+ char buf[1024];
+ if (!GetEnvironmentVariable ("CYGWIN_FORK_PIDS", buf, 1024))
+ return;
+ pid_t pid;
+ char *p, *pe;
+ for (p = buf; (pid = strtol (p, &pe, 10)); p = pe)
+ fork_pids[npid_max++] = pid;
+}
+#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
+ return fork ();
+#else
+ sigframe thisframe;
+ vfork_save *vf = get_vfork_val ();
+ char **esp, **pp;
+
+ if (vf == NULL)
+ vf = vfork_storage.create ();
+ else if (vf->pid)
+ return fork ();
+
+ 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;
+ int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
+ debug_printf ("%d = vfork()", res);
+ return res;
+ }
+
+ vf = get_vfork_val ();
+
+ for (pp = (char **)vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *esp = *pp;
+
+ thisframe.init (mainthread);
+ cygheap->fdtab.vfork_parent_restore ();
+
+ myself->ctty = vf->ctty;
+ myself->sid = vf->sid;
+ myself->pgid = vf->pgid;
+
+ if (vf->pid < 0)
+ {
+ int exitval = vf->exitval;
+ vf->pid = 0;
+ if ((vf->pid = fork ()) == 0)
+ exit (exitval);
+ }
+
+ int pid = vf->pid;
+ vf->pid = 0;
+ debug_printf ("exiting vfork, pid %d", pid);
+ sig_dispatch_pending ();
+ return pid;
+#endif
+}