|
|
|
@ -173,114 +173,184 @@ sync_with_child (PROCESS_INFORMATION &pi, HANDLE subproc_ready,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug_printf ("child signalled me");
|
|
|
|
|
if (hang_child)
|
|
|
|
|
{
|
|
|
|
|
int n = SuspendThread (pi.hThread);
|
|
|
|
|
debug_printf ("suspend count %d", n); \
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
resume_child (PROCESS_INFORMATION &pi, HANDLE forker_finished)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
debug_printf ("here");
|
|
|
|
|
SetEvent (forker_finished);
|
|
|
|
|
|
|
|
|
|
rc = ResumeThread (pi.hThread);
|
|
|
|
|
|
|
|
|
|
debug_printf ("rc %d", rc);
|
|
|
|
|
if (rc == 1)
|
|
|
|
|
return 1; // Successful resumption
|
|
|
|
|
|
|
|
|
|
/* Can't resume the thread. Not sure why this would happen unless
|
|
|
|
|
there's a bug in the system. Things seem to be working OK now
|
|
|
|
|
though, so flag this with EAGAIN, but print a message on the
|
|
|
|
|
console. */
|
|
|
|
|
small_printf ("fork: ResumeThread failed, rc = %d, %E\n", rc);
|
|
|
|
|
set_errno (EAGAIN);
|
|
|
|
|
syscall_printf ("-1 = fork(), ResumeThread failed");
|
|
|
|
|
TerminateProcess (pi.hProcess, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
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. */
|
|
|
|
|
#define sync_with_parent(s, hang_self) \
|
|
|
|
|
((void) ({ \
|
|
|
|
|
debug_printf ("signalling parent: %s", s); \
|
|
|
|
|
/* Tell our parent we're waiting. */ \
|
|
|
|
|
if (!SetEvent (child_proc_info->subproc_ready)) \
|
|
|
|
|
api_fatal ("fork child - SetEvent failed, %E"); \
|
|
|
|
|
if (hang_self) \
|
|
|
|
|
{ \
|
|
|
|
|
/* 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 (child_proc_info->forker_finished, FORK_WAIT_TIMEOUT); \
|
|
|
|
|
switch (psync_rc) \
|
|
|
|
|
{ \
|
|
|
|
|
case WAIT_TIMEOUT: \
|
|
|
|
|
api_fatal ("sync_with_parent - WFSO timed out"); \
|
|
|
|
|
break; \
|
|
|
|
|
case WAIT_FAILED: \
|
|
|
|
|
if (GetLastError () == ERROR_INVALID_HANDLE && \
|
|
|
|
|
WaitForSingleObject (child_proc_info->forker_finished, 1) != WAIT_FAILED) \
|
|
|
|
|
break; \
|
|
|
|
|
api_fatal ("sync_with_parent - WFSO failed, fork_finished %p, %E", child_proc_info->forker_finished); \
|
|
|
|
|
break; \
|
|
|
|
|
default: \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
debug_printf ("awake"); \
|
|
|
|
|
} \
|
|
|
|
|
0; \
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
static volatile void grow_stack_slack();
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
stack_dummy (int here)
|
|
|
|
|
static void __stdcall
|
|
|
|
|
sync_with_parent(const char *s, bool hang_self)
|
|
|
|
|
{
|
|
|
|
|
return &here;
|
|
|
|
|
debug_printf ("signalling parent: %s", s);
|
|
|
|
|
/* Tell our parent we're waiting. */
|
|
|
|
|
if (!SetEvent (child_proc_info->subproc_ready))
|
|
|
|
|
api_fatal ("fork child - SetEvent failed, %E");
|
|
|
|
|
if (hang_self)
|
|
|
|
|
{
|
|
|
|
|
HANDLE h = child_proc_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");
|
|
|
|
|
break;
|
|
|
|
|
case WAIT_FAILED:
|
|
|
|
|
if (GetLastError () == ERROR_INVALID_HANDLE &&
|
|
|
|
|
WaitForSingleObject (child_proc_info->forker_finished, 1) != WAIT_FAILED)
|
|
|
|
|
break;
|
|
|
|
|
api_fatal ("WFSO failed, fork_finished %p, %E", child_proc_info->forker_finished);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
debug_printf ("no problems");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int
|
|
|
|
|
fork ()
|
|
|
|
|
static int __stdcall
|
|
|
|
|
fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
DWORD rc;
|
|
|
|
|
HANDLE hParent;
|
|
|
|
|
HANDLE subproc_ready, forker_finished;
|
|
|
|
|
void *stack_here;
|
|
|
|
|
int x;
|
|
|
|
|
PROCESS_INFORMATION pi = {0, NULL, 0, 0};
|
|
|
|
|
static NO_COPY HANDLE last_fork_proc = NULL;
|
|
|
|
|
debug_printf ("child is running. pid %d, ppid %d, stack here %p",
|
|
|
|
|
myself->pid, myself->ppid, __builtin_frame_address (0));
|
|
|
|
|
child_info_fork ch;
|
|
|
|
|
stack_base (ch);
|
|
|
|
|
|
|
|
|
|
/* Restore the inheritance state as in parent
|
|
|
|
|
Don't call setuid here! The flags are already set. */
|
|
|
|
|
if (myself->impersonated)
|
|
|
|
|
{
|
|
|
|
|
debug_printf ("Impersonation of child, token: %d", myself->token);
|
|
|
|
|
if (myself->token == INVALID_HANDLE_VALUE)
|
|
|
|
|
RevertToSelf (); // probably not needed
|
|
|
|
|
else if (!ImpersonateLoggedOnUser (myself->token))
|
|
|
|
|
system_printf ("Impersonate for forked child failed: %E");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sync_with_parent ("after longjmp.", TRUE);
|
|
|
|
|
debug_printf ("hParent %p", hParent);
|
|
|
|
|
ProtectHandle (hParent);
|
|
|
|
|
// small_printf ("child 1 first_dll %p, load_dlls %d\n", 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 (child_proc_info->stacksize)
|
|
|
|
|
{
|
|
|
|
|
((DWORD *)child_proc_info->stackbottom)[-17] = (DWORD)do_exit;
|
|
|
|
|
((DWORD *)child_proc_info->stackbottom)[-15] = (DWORD)0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
|
|
/* FIXME: something is broken when copying the stack from the parent
|
|
|
|
|
to the child; we try various tricks here to make sure that the
|
|
|
|
|
stack is good enough to prevent page faults, but the true cause
|
|
|
|
|
is still unknown. DJ */
|
|
|
|
|
volatile char dummy[4096];
|
|
|
|
|
dummy[0] = dummy[4095] = 0; // Just to leave some slack in the stack
|
|
|
|
|
fdtab.fixup_after_fork (hParent);
|
|
|
|
|
signal_fixup_after_fork ();
|
|
|
|
|
exec_fixup_after_fork ();
|
|
|
|
|
|
|
|
|
|
grow_stack_slack ();
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
|
|
debug_printf ("entering");
|
|
|
|
|
/* Calculate how much of stack to copy to child */
|
|
|
|
|
stack_here = stack_dummy (0);
|
|
|
|
|
|
|
|
|
|
if (ISSTATE(myself, PID_SPLIT_HEAP))
|
|
|
|
|
/* 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)
|
|
|
|
|
sync_with_parent ("performed fork fixup.", FALSE);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
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");
|
|
|
|
|
dlls.load_after_fork (hParent, first_dll);
|
|
|
|
|
sync_with_parent ("loaded dlls", TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ForceCloseHandle (hParent);
|
|
|
|
|
(void) ForceCloseHandle (child_proc_info->subproc_ready);
|
|
|
|
|
(void) ForceCloseHandle (child_proc_info->forker_finished);
|
|
|
|
|
|
|
|
|
|
if (recreate_mmaps_after_fork (myself->mmap_ptr))
|
|
|
|
|
api_fatal ("recreate_mmaps_after_fork_failed");
|
|
|
|
|
|
|
|
|
|
/* 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 ();
|
|
|
|
|
|
|
|
|
|
/* Initialize signal/process handling */
|
|
|
|
|
sigproc_init ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __stdcall
|
|
|
|
|
fork_parent (HANDLE& hParent, dll *&first_dll, bool& load_dlls, child_info_fork &ch)
|
|
|
|
|
{
|
|
|
|
|
HANDLE subproc_ready, forker_finished;
|
|
|
|
|
void *stack_here = &hParent;
|
|
|
|
|
DWORD rc;
|
|
|
|
|
PROCESS_INFORMATION pi = {0, NULL, 0, 0};
|
|
|
|
|
static NO_COPY HANDLE last_fork_proc = NULL;
|
|
|
|
|
|
|
|
|
|
subproc_init ();
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUGGING
|
|
|
|
|
/* The ProtectHandle call allocates memory so we need to make sure
|
|
|
|
|
that enough is set aside here so that the sbrk pointer does not
|
|
|
|
|
move when ProtectHandle is called after the child is started.
|
|
|
|
|
Otherwise the sbrk pointers in the parent will not agree with
|
|
|
|
|
the child and when user_data is (regrettably) copied over,
|
|
|
|
|
the user_data->ptr field will not be accurate. */
|
|
|
|
|
free (malloc (4096));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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 = CreateFileA ("CONOUT$", GENERIC_WRITE,
|
|
|
|
|
FILE_SHARE_WRITE, &sec_none_nih,
|
|
|
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
syscall_printf ("CreateProcessA (%s, %s,0,0,1,%x, 0,0,%p,%p)",
|
|
|
|
|
myself->progname, myself->progname, c_flags, &si, &pi);
|
|
|
|
|
if (console_handle != INVALID_HANDLE_VALUE && console_handle != 0)
|
|
|
|
|
CloseHandle (console_handle);
|
|
|
|
|
else
|
|
|
|
|
c_flags |= DETACHED_PROCESS;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -288,332 +358,202 @@ fork ()
|
|
|
|
|
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. */
|
|
|
|
|
dll *first_dll = dlls.start.next;
|
|
|
|
|
int load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
|
|
|
|
|
first_dll = dlls.start.next;
|
|
|
|
|
load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
|
|
|
|
|
|
|
|
|
|
static child_info_fork ch;
|
|
|
|
|
x = setjmp (ch.jmp);
|
|
|
|
|
/* This will help some of the confusion. */
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
|
|
|
|
|
if (x == 0)
|
|
|
|
|
subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL);
|
|
|
|
|
if (subproc_ready == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* This will help some of the confusion. */
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
|
|
|
|
|
subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL);
|
|
|
|
|
forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
|
|
|
|
|
ProtectHandle (subproc_ready);
|
|
|
|
|
ProtectHandle (forker_finished);
|
|
|
|
|
|
|
|
|
|
/* If we didn't obtain all the resources we need to fork, allow the program
|
|
|
|
|
to continue, but record the fact that fork won't work. */
|
|
|
|
|
if (forker_finished == NULL || subproc_ready == NULL)
|
|
|
|
|
{
|
|
|
|
|
system_printf ("unable to allocate fork() resources.");
|
|
|
|
|
system_printf ("fork() disabled.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subproc_init ();
|
|
|
|
|
|
|
|
|
|
debug_printf ("about to call setjmp");
|
|
|
|
|
/* Parent. */
|
|
|
|
|
#ifdef DEBUGGING
|
|
|
|
|
/* The ProtectHandle call allocates memory so we need to make sure
|
|
|
|
|
that enough is set aside here so that the sbrk pointer does not
|
|
|
|
|
move when ProtectHandle is called after the child is started.
|
|
|
|
|
Otherwise the sbrk pointers in the parent will not agree with
|
|
|
|
|
the child and when user_data is (regrettably) copied over,
|
|
|
|
|
the user_data->ptr field will not be accurate. */
|
|
|
|
|
free (malloc (4096));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
init_child_info (PROC_FORK1, &ch, 1, subproc_ready);
|
|
|
|
|
|
|
|
|
|
ch.forker_finished = forker_finished;
|
|
|
|
|
ch.heaptop = user_data->heaptop;
|
|
|
|
|
ch.heapbase = user_data->heapbase;
|
|
|
|
|
ch.heapptr = user_data->heapptr;
|
|
|
|
|
|
|
|
|
|
stack_base (ch);
|
|
|
|
|
|
|
|
|
|
STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
|
|
|
|
|
|
|
|
|
|
si.cb = sizeof (STARTUPINFO);
|
|
|
|
|
si.lpReserved2 = (LPBYTE)&ch;
|
|
|
|
|
si.cbReserved2 = sizeof(ch);
|
|
|
|
|
|
|
|
|
|
int c_flags = GetPriorityClass (hMainProc) /*|
|
|
|
|
|
CREATE_NEW_PROCESS_GROUP*/;
|
|
|
|
|
|
|
|
|
|
/* If we don't have a console, then don't create a console for the
|
|
|
|
|
child either. */
|
|
|
|
|
HANDLE console_handle = CreateFileA ("CONOUT$", GENERIC_WRITE,
|
|
|
|
|
FILE_SHARE_WRITE, &sec_none_nih,
|
|
|
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
syscall_printf ("CreateProcessA (%s, %s,0,0,1,%x, 0,0,%p,%p)",
|
|
|
|
|
myself->progname, myself->progname, c_flags, &si, &pi);
|
|
|
|
|
if (console_handle != INVALID_HANDLE_VALUE && console_handle != 0)
|
|
|
|
|
CloseHandle (console_handle);
|
|
|
|
|
else
|
|
|
|
|
c_flags |= DETACHED_PROCESS;
|
|
|
|
|
|
|
|
|
|
hParent = NULL;
|
|
|
|
|
if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hParent, 0, 1,
|
|
|
|
|
DUPLICATE_SAME_ACCESS))
|
|
|
|
|
{
|
|
|
|
|
system_printf ("couldn't create handle to myself for child, %E");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove impersonation */
|
|
|
|
|
uid_t uid = geteuid();
|
|
|
|
|
if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
|
|
|
|
|
seteuid (myself->orig_uid);
|
|
|
|
|
|
|
|
|
|
char sa_buf[1024];
|
|
|
|
|
rc = CreateProcessA (myself->progname, /* image to run */
|
|
|
|
|
myself->progname, /* what we send in arg0 */
|
|
|
|
|
allow_ntsec ? sec_user (sa_buf) : &sec_none_nih,
|
|
|
|
|
allow_ntsec ? sec_user (sa_buf) : &sec_none_nih,
|
|
|
|
|
TRUE, /* inherit handles from parent */
|
|
|
|
|
c_flags,
|
|
|
|
|
NULL, /* environment filled in later */
|
|
|
|
|
0, /* use current drive/directory */
|
|
|
|
|
&si,
|
|
|
|
|
&pi);
|
|
|
|
|
|
|
|
|
|
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 subproc_ready event, %E");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rc)
|
|
|
|
|
{
|
|
|
|
|
__seterrno ();
|
|
|
|
|
syscall_printf ("-1 = fork(), CreateProcessA failed");
|
|
|
|
|
ForceCloseHandle(subproc_ready);
|
|
|
|
|
ForceCloseHandle(forker_finished);
|
|
|
|
|
subproc_ready = forker_finished = NULL;
|
|
|
|
|
/* Restore impersonation */
|
|
|
|
|
if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
|
|
|
|
|
seteuid (uid);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ProtectHandle (subproc_ready);
|
|
|
|
|
ProtectHandle (forker_finished);
|
|
|
|
|
|
|
|
|
|
pinfo forked (cygwin_pid (pi.dwProcessId), 1);
|
|
|
|
|
init_child_info (PROC_FORK1, &ch, 1, subproc_ready);
|
|
|
|
|
|
|
|
|
|
/* Initialize things that are done later in dll_crt0_1 that aren't done
|
|
|
|
|
for the forkee. */
|
|
|
|
|
strcpy(forked->progname, myself->progname);
|
|
|
|
|
ch.forker_finished = forker_finished;
|
|
|
|
|
ch.heaptop = user_data->heaptop;
|
|
|
|
|
ch.heapbase = user_data->heapbase;
|
|
|
|
|
ch.heapptr = user_data->heapptr;
|
|
|
|
|
|
|
|
|
|
stack_base (ch);
|
|
|
|
|
|
|
|
|
|
si.cb = sizeof (STARTUPINFO);
|
|
|
|
|
si.lpReserved2 = (LPBYTE)&ch;
|
|
|
|
|
si.cbReserved2 = sizeof(ch);
|
|
|
|
|
|
|
|
|
|
/* Remove impersonation */
|
|
|
|
|
uid_t uid;
|
|
|
|
|
uid = geteuid();
|
|
|
|
|
if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
|
|
|
|
|
seteuid (myself->orig_uid);
|
|
|
|
|
|
|
|
|
|
ch.parent = hParent;
|
|
|
|
|
ch.cygheap = cygheap;
|
|
|
|
|
ch.cygheap_max = cygheap_max;
|
|
|
|
|
|
|
|
|
|
char sa_buf[1024];
|
|
|
|
|
rc = CreateProcessA (myself->progname, /* image to run */
|
|
|
|
|
myself->progname, /* what we send in arg0 */
|
|
|
|
|
allow_ntsec ? sec_user (sa_buf) : &sec_none_nih,
|
|
|
|
|
allow_ntsec ? sec_user (sa_buf) : &sec_none_nih,
|
|
|
|
|
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 (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
|
|
|
|
|
seteuid (uid);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProtectHandle (pi.hThread);
|
|
|
|
|
/* Protect the handle but name it similarly to the way it will
|
|
|
|
|
be called in subproc handling. */
|
|
|
|
|
ProtectHandle1 (pi.hProcess, childhProc);
|
|
|
|
|
if (os_being_run != winNT)
|
|
|
|
|
{
|
|
|
|
|
if (last_fork_proc)
|
|
|
|
|
CloseHandle (last_fork_proc);
|
|
|
|
|
if (!DuplicateHandle (hMainProc, pi.hProcess, hMainProc, &last_fork_proc,
|
|
|
|
|
0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
|
|
|
system_printf ("couldn't create last_fork_proc, %E");
|
|
|
|
|
}
|
|
|
|
|
pinfo forked (cygwin_pid (pi.dwProcessId), 1);
|
|
|
|
|
|
|
|
|
|
/* Fill in fields in the child's process table entry. */
|
|
|
|
|
forked->ppid = myself->pid;
|
|
|
|
|
forked->hProcess = pi.hProcess;
|
|
|
|
|
forked->dwProcessId = pi.dwProcessId;
|
|
|
|
|
forked->uid = myself->uid;
|
|
|
|
|
forked->gid = myself->gid;
|
|
|
|
|
forked->pgid = myself->pgid;
|
|
|
|
|
forked->sid = myself->sid;
|
|
|
|
|
forked->ctty = myself->ctty;
|
|
|
|
|
forked->umask = myself->umask;
|
|
|
|
|
forked->copysigs(myself);
|
|
|
|
|
forked->process_state |= PID_INITIALIZING |
|
|
|
|
|
(myself->process_state & PID_USETTY);
|
|
|
|
|
memcpy (forked->username, myself->username, MAX_USER_NAME);
|
|
|
|
|
if (myself->use_psid)
|
|
|
|
|
{
|
|
|
|
|
memcpy (forked->psid, myself->psid, MAX_SID_LEN);
|
|
|
|
|
forked->use_psid = 1;
|
|
|
|
|
}
|
|
|
|
|
memcpy (forked->logsrv, myself->logsrv, MAX_HOST_NAME);
|
|
|
|
|
memcpy (forked->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1);
|
|
|
|
|
forked->token = myself->token;
|
|
|
|
|
forked->impersonated = myself->impersonated;
|
|
|
|
|
forked->orig_uid = myself->orig_uid;
|
|
|
|
|
forked->orig_gid = myself->orig_gid;
|
|
|
|
|
forked->real_uid = myself->real_uid;
|
|
|
|
|
forked->real_gid = myself->real_gid;
|
|
|
|
|
strcpy (forked->root, myself->root);
|
|
|
|
|
forked->rootlen = myself->rootlen;
|
|
|
|
|
set_child_mmap_ptr (forked);
|
|
|
|
|
/* Initialize things that are done later in dll_crt0_1 that aren't done
|
|
|
|
|
for the forkee. */
|
|
|
|
|
strcpy(forked->progname, myself->progname);
|
|
|
|
|
|
|
|
|
|
/* Wait for subproc to initialize itself. */
|
|
|
|
|
if (!sync_with_child(pi, subproc_ready, TRUE, "waiting for longjmp"))
|
|
|
|
|
/* Restore impersonation */
|
|
|
|
|
if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
|
|
|
|
|
seteuid (uid);
|
|
|
|
|
|
|
|
|
|
ProtectHandle (pi.hThread);
|
|
|
|
|
/* Protect the handle but name it similarly to the way it will
|
|
|
|
|
be called in subproc handling. */
|
|
|
|
|
ProtectHandle1 (pi.hProcess, childhProc);
|
|
|
|
|
if (os_being_run != winNT)
|
|
|
|
|
{
|
|
|
|
|
if (last_fork_proc)
|
|
|
|
|
CloseHandle (last_fork_proc);
|
|
|
|
|
if (!DuplicateHandle (hMainProc, pi.hProcess, hMainProc, &last_fork_proc,
|
|
|
|
|
0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
|
|
|
system_printf ("couldn't create last_fork_proc, %E");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill in fields in the child's process table entry. */
|
|
|
|
|
forked->ppid = myself->pid;
|
|
|
|
|
forked->hProcess = pi.hProcess;
|
|
|
|
|
forked->dwProcessId = pi.dwProcessId;
|
|
|
|
|
forked->uid = myself->uid;
|
|
|
|
|
forked->gid = myself->gid;
|
|
|
|
|
forked->pgid = myself->pgid;
|
|
|
|
|
forked->sid = myself->sid;
|
|
|
|
|
forked->ctty = myself->ctty;
|
|
|
|
|
forked->umask = myself->umask;
|
|
|
|
|
forked->copysigs(myself);
|
|
|
|
|
forked->process_state |= PID_INITIALIZING |
|
|
|
|
|
(myself->process_state & PID_USETTY);
|
|
|
|
|
memcpy (forked->username, myself->username, MAX_USER_NAME);
|
|
|
|
|
if (myself->use_psid)
|
|
|
|
|
{
|
|
|
|
|
memcpy (forked->psid, myself->psid, MAX_SID_LEN);
|
|
|
|
|
forked->use_psid = 1;
|
|
|
|
|
}
|
|
|
|
|
memcpy (forked->logsrv, myself->logsrv, MAX_HOST_NAME);
|
|
|
|
|
memcpy (forked->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1);
|
|
|
|
|
forked->token = myself->token;
|
|
|
|
|
forked->impersonated = myself->impersonated;
|
|
|
|
|
forked->orig_uid = myself->orig_uid;
|
|
|
|
|
forked->orig_gid = myself->orig_gid;
|
|
|
|
|
forked->real_uid = myself->real_uid;
|
|
|
|
|
forked->real_gid = myself->real_gid;
|
|
|
|
|
strcpy (forked->root, myself->root);
|
|
|
|
|
forked->rootlen = myself->rootlen;
|
|
|
|
|
set_child_mmap_ptr (forked);
|
|
|
|
|
|
|
|
|
|
/* 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,
|
|
|
|
|
ch.heapbase, ch.heapptr,
|
|
|
|
|
stack_here, ch.stackbottom,
|
|
|
|
|
dll_data_start, dll_data_end,
|
|
|
|
|
dll_bss_start, dll_bss_end, NULL);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
forked.remember ();
|
|
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
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,
|
|
|
|
|
ch.heapbase, ch.heapptr,
|
|
|
|
|
stack_here, ch.stackbottom,
|
|
|
|
|
dll_data_start, dll_data_end,
|
|
|
|
|
dll_bss_start, dll_bss_end, NULL);
|
|
|
|
|
|
|
|
|
|
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 ())
|
|
|
|
|
/* write memory of reloaded dlls */
|
|
|
|
|
for (dll *d = dlls.istart (DLL_LOAD); 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,
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
forked.remember ();
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
res = forked->pid;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/**** Child *****/
|
|
|
|
|
|
|
|
|
|
/* We arrive here via a longjmp from "crt0". */
|
|
|
|
|
(void) stack_dummy (0); // Just to make sure
|
|
|
|
|
debug_printf ("child is running %d", x);
|
|
|
|
|
|
|
|
|
|
debug_printf ("pid %d, ppid %d", x, myself->ppid);
|
|
|
|
|
|
|
|
|
|
/* Restore the inheritance state as in parent
|
|
|
|
|
Don't call setuid here! The flags are already set. */
|
|
|
|
|
if (myself->impersonated)
|
|
|
|
|
{
|
|
|
|
|
debug_printf ("Impersonation of child, token: %d", myself->token);
|
|
|
|
|
if (myself->token == INVALID_HANDLE_VALUE)
|
|
|
|
|
RevertToSelf (); // probably not needed
|
|
|
|
|
else if (!ImpersonateLoggedOnUser (myself->token))
|
|
|
|
|
system_printf ("Impersonate for forked child failed: %E");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sync_with_parent ("after longjmp.", TRUE);
|
|
|
|
|
ProtectHandle (hParent);
|
|
|
|
|
|
|
|
|
|
#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 (child_proc_info->stacksize)
|
|
|
|
|
{
|
|
|
|
|
((DWORD *)child_proc_info->stackbottom)[-17] = (DWORD)do_exit;
|
|
|
|
|
((DWORD *)child_proc_info->stackbottom)[-15] = (DWORD)0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
|
|
cygheap_fixup_in_child (hParent, 0);
|
|
|
|
|
fdtab.fixup_after_fork (hParent);
|
|
|
|
|
signal_fixup_after_fork ();
|
|
|
|
|
exec_fixup_after_fork ();
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
sync_with_parent ("performed fork fixup.", FALSE);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dlls.load_after_fork (hParent, first_dll);
|
|
|
|
|
sync_with_parent ("loaded dlls", TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ForceCloseHandle (hParent);
|
|
|
|
|
(void) ForceCloseHandle (child_proc_info->subproc_ready);
|
|
|
|
|
(void) ForceCloseHandle (child_proc_info->forker_finished);
|
|
|
|
|
|
|
|
|
|
if (recreate_mmaps_after_fork (myself->mmap_ptr))
|
|
|
|
|
api_fatal ("recreate_mmaps_after_fork_failed");
|
|
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
|
/* 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 ();
|
|
|
|
|
|
|
|
|
|
/* Initialize signal/process handling */
|
|
|
|
|
sigproc_init ();
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
syscall_printf ("%d = fork()", res);
|
|
|
|
|
return res;
|
|
|
|
|
return forked->pid;
|
|
|
|
|
|
|
|
|
|
/* Common cleanup code for failure cases */
|
|
|
|
|
cleanup:
|
|
|
|
@ -626,15 +566,48 @@ cleanup:
|
|
|
|
|
ForceCloseHandle (subproc_ready);
|
|
|
|
|
if (forker_finished)
|
|
|
|
|
ForceCloseHandle (forker_finished);
|
|
|
|
|
forker_finished = subproc_ready = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static volatile void
|
|
|
|
|
grow_stack_slack ()
|
|
|
|
|
extern "C" int
|
|
|
|
|
fork ()
|
|
|
|
|
{
|
|
|
|
|
volatile char dummy[16384];
|
|
|
|
|
dummy[0] = dummy[16383] = 0; // Just to make some slack in the stack
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
HANDLE hParent;
|
|
|
|
|
dll *first_dll;
|
|
|
|
|
bool load_dlls;
|
|
|
|
|
} grouped;
|
|
|
|
|
|
|
|
|
|
int res;
|
|
|
|
|
int x;
|
|
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
|
|
|
|
|
// grow_stack_slack ();
|
|
|
|
|
|
|
|
|
|
debug_printf ("entering");
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
child_info_fork ch;
|
|
|
|
|
x = setjmp (ch.jmp);
|
|
|
|
|
|
|
|
|
|
if (x != 0)
|
|
|
|
|
res = fork_child (grouped.hParent, grouped.first_dll, grouped.load_dlls);
|
|
|
|
|
else
|
|
|
|
|
res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, ch);
|
|
|
|
|
|
|
|
|
|
MALLOC_CHECK;
|
|
|
|
|
syscall_printf ("%d = fork()", res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef NEWVFORK
|
|
|
|
|