* sigproc.cc (mychild): Reimplement as list scan.

(proc_subproc): Don't mess with pinfo if it's myself.
* child_info.h (child_info_types): Label enum for _PROC constants.
(child_info::child_info): New constructor.
(child_info::~child_info): New destructor.
(child_info::sync): Declare new function.
(child_info_fork::child_info_fork): New constructor.
(child_info_spawn::child_info_spawn): Remove old constructor.
(child_info_spawn::child_info_spawn): New constructor.
* dcrt0.cc (dll_crt0_0): Use correct sizeof when doing sanity check on passed
in child_info.  Signal readiness to parent when not forking (and not spawning).
* fork.cc (sync_with_child): Delete.
(resume_child): Remove extra argument.
(sync_with_parent): Use child_info method to sync with parent.
(fork_child): Don't close fork_info->subproc_ready since that is now handled by
the destructor.
(fork_parent): Remove subproc_ready stuff.  Use child_info sync method for
waiting..  Set start time here for child.  Rename "forked" to "child".
(fork): Check ch.subproc_ready for validity here.
* pinfo.h (_pinfo::exec_sendsig): Temp storage for exec stub which may be
staying around to handle non-cygwin captive process.
(_pinfo::exec_dwProcessId): Ditto.
(_pinfo::_lock): Renamed from lock.
(_pinfo::lock): New method.
(_pinfo::unlock): Ditto.
(_pinfo::initialize_lock): Ditto.
* pinfo.cc (set_myself): Use initialize_lock method to initialize myself lock.
Set "exec" fields in _pinfo to zero to indicate that we've started
successfully.  Set start time here when appropriate.
(_pinfo::commune_send): Use pinfo lock/unlock methods.
(proc_waiter): Remove special case for non-cywin processes.  Reinstitute
handling for PID_NOCLDSTOP.
* sigproc.cc (proc_subproc): Set proper EAGAIN errno when process table is
filled.
(sig_send): Use exec_* fields from _pinfo for sending signals if the the _pinfo
sendsig never materializes.
(child_info::child_info): New constructor, renamed from init_child_info.
Zeroes child_info structure and sets appropriate fields in structure based on
chtype.
(child_info::~child_info): New destructor.  Closes subproc_ready if it exists.
(child_info_fork::child_info_fork): New constructor.
(child_info_spawn::child_info_spawn): New constructor.
(child_info::ready): New function.  Signals parent when child is ready.
(child_info::sync): New function.  Wait for child to signal us or process to
die.
(remove_proc): Remove closing of hProcess since this should now be handled
shortly after process creation.
* spawn.cc (spawn_guts): Use child_info_spawn constructor rather than
init_child_info.  Save exec_sendsig and exec_dwProcessId in execing _pinfo.
Rely on child_info constructor to properly set parent_wr_proc_pipe in ciresrv.
Revert to previous determination on whether to start a process in suspended
mode.  Remove reparenting stuff.  Just keep a stub around if starting a
non-cygwin process.
This commit is contained in:
Christopher Faylor 2004-12-05 19:41:26 +00:00
parent d54b79d351
commit 54dd79bb44
9 changed files with 501 additions and 376 deletions

View File

@ -1,3 +1,65 @@
2004-12-05 Christopher Faylor <cgf@timesys.com>
* sigproc.cc (mychild): Reimplement as list scan.
(proc_subproc): Don't mess with pinfo if it's myself.
2004-12-05 Christopher Faylor <cgf@timesys.com>
* child_info.h (child_info_types): Label enum for _PROC constants.
(child_info::child_info): New constructor.
(child_info::~child_info): New destructor.
(child_info::sync): Declare new function.
(child_info_fork::child_info_fork): New constructor.
(child_info_spawn::child_info_spawn): Remove old constructor.
(child_info_spawn::child_info_spawn): New constructor.
* dcrt0.cc (dll_crt0_0): Use correct sizeof when doing sanity check on
passed in child_info. Signal readiness to parent when not forking (and
not spawning).
* fork.cc (sync_with_child): Delete.
(resume_child): Remove extra argument.
(sync_with_parent): Use child_info method to sync with parent.
(fork_child): Don't close fork_info->subproc_ready since that is now
handled by the destructor.
(fork_parent): Remove subproc_ready stuff. Use child_info sync method
for waiting.. Set start time here for child. Rename "forked" to
"child".
(fork): Check ch.subproc_ready for validity here.
* pinfo.h (_pinfo::exec_sendsig): Temp storage for exec stub which may
be staying around to handle non-cygwin captive process.
(_pinfo::exec_dwProcessId): Ditto.
(_pinfo::_lock): Renamed from lock.
(_pinfo::lock): New method.
(_pinfo::unlock): Ditto.
(_pinfo::initialize_lock): Ditto.
* pinfo.cc (set_myself): Use initialize_lock method to initialize
myself lock. Set "exec" fields in _pinfo to zero to indicate that
we've started successfully. Set start time here when appropriate.
(_pinfo::commune_send): Use pinfo lock/unlock methods.
(proc_waiter): Remove special case for non-cywin processes.
Reinstitute handling for PID_NOCLDSTOP.
* sigproc.cc (proc_subproc): Set proper EAGAIN errno when process table
is filled.
(sig_send): Use exec_* fields from _pinfo for sending signals if the
the _pinfo sendsig never materializes.
(child_info::child_info): New constructor, renamed from init_child_info.
Zeroes child_info structure and sets appropriate fields in structure
based on chtype.
(child_info::~child_info): New destructor. Closes subproc_ready if it
exists.
(child_info_fork::child_info_fork): New constructor.
(child_info_spawn::child_info_spawn): New constructor.
(child_info::ready): New function. Signals parent when child is ready.
(child_info::sync): New function. Wait for child to signal us or
process to die.
(remove_proc): Remove closing of hProcess since this should now be
handled shortly after process creation.
* spawn.cc (spawn_guts): Use child_info_spawn constructor rather than
init_child_info. Save exec_sendsig and exec_dwProcessId in execing
_pinfo. Rely on child_info constructor to properly set
parent_wr_proc_pipe in ciresrv. Revert to previous determination on
whether to start a process in suspended mode. Remove reparenting
stuff. Just keep a stub around if starting a non-cygwin process.
2004-12-05 Bas van Gompel <cygwin-patch@bavag.tmfweb.nl>
* fhandler.cc (fhandler_base::read): Remove superfluous check in

View File

@ -10,7 +10,7 @@ details. */
#include <setjmp.h>
enum
enum child_info_types
{
_PROC_EXEC,
_PROC_SPAWN,
@ -51,6 +51,10 @@ public:
HANDLE cygheap_h;
HANDLE parent_wr_proc_pipe;
unsigned fhandler_union_cb;
child_info (unsigned, child_info_types);
~child_info ();
void ready (bool);
bool sync (pinfo&, DWORD);
};
class mount_info;
@ -64,6 +68,7 @@ public:
jmp_buf jmp; // where child will jump to
void *stacktop; // location of top of parent stack
void *stackbottom; // location of bottom of parent stack
child_info_fork ();
};
class fhandler_base;
@ -84,7 +89,6 @@ class child_info_spawn: public child_info
public:
cygheap_exec_info *moreinfo;
child_info_spawn (): moreinfo (NULL) {}
~child_info_spawn ()
{
if (moreinfo)
@ -101,6 +105,7 @@ public:
cfree (moreinfo);
}
}
child_info_spawn (child_info_types);
};
void __stdcall init_child_info (DWORD, child_info *, HANDLE);

View File

@ -618,13 +618,15 @@ dll_crt0_0 ()
case _PROC_SPAWN:
case _PROC_EXEC:
if (!should_be_cb)
should_be_cb = sizeof (child_info);
should_be_cb = sizeof (child_info_spawn);
if (should_be_cb != child_proc_info->cb)
multiple_cygwin_problem ("proc size", child_proc_info->cb, should_be_cb);
else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb)
multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union));
else
{
if (child_proc_info->type != _PROC_FORK)
child_proc_info->ready (true);
cygwin_user_h = child_proc_info->user_h;
break;
}

View File

@ -107,68 +107,8 @@ fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
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 %u(%p) died before initialization with status code %p",
cygwin_pid (pi.dwProcessId), pi.hProcess, errcode);
system_printf ("*** child state %s", s);
#ifdef DEBUGGING
try_to_debug ();
#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)
resume_child (HANDLE forker_finished)
{
SetEvent (forker_finished);
debug_printf ("signalled child");
@ -182,9 +122,7 @@ 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);
fork_info->ready (false);
if (hang_self)
{
HANDLE h = fork_info->forker_finished;
@ -281,7 +219,6 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
}
ForceCloseHandle (hParent);
(void) ForceCloseHandle1 (fork_info->subproc_ready, subproc_ready);
(void) ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
_my_tls.fixup_after_fork ();
@ -308,7 +245,7 @@ slow_pid_reuse (HANDLE h)
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
/* 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. */
@ -330,7 +267,7 @@ 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;
HANDLE forker_finished;
DWORD rc;
PROCESS_INFORMATION pi = {0, NULL, 0, 0};
@ -379,35 +316,22 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
/* 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, subproc_ready);
ch.forker_finished = forker_finished;
ch.parent_wr_proc_pipe = myself->wr_proc_pipe == INVALID_HANDLE_VALUE
? NULL : myself->wr_proc_pipe;
stack_base (ch);
si.cb = sizeof (STARTUPINFO);
si.lpReserved2 = (LPBYTE)&ch;
si.lpReserved2 = (LPBYTE) &ch;
si.cbReserved2 = sizeof (ch);
/* Remove impersonation */
@ -437,7 +361,6 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
{
__seterrno ();
syscall_printf ("CreateProcessA failed, %E");
ForceCloseHandle (subproc_ready);
ForceCloseHandle (forker_finished);
/* Restore impersonation */
cygheap->user.reimpersonate ();
@ -457,10 +380,11 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
ResumeThread (pi.hThread);
}
int forked_pid = cygwin_pid (pi.dwProcessId);
pinfo forked (forked_pid, 1);
int child_pid = cygwin_pid (pi.dwProcessId);
pinfo child (child_pid, 1);
child->start_time = time (NULL); /* Register child's starting time. */
if (!forked)
if (!child)
{
syscall_printf ("pinfo failed");
if (get_errno () != ENOMEM)
@ -470,7 +394,7 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
/* Initialize things that are done later in dll_crt0_1 that aren't done
for the forkee. */
strcpy (forked->progname, myself->progname);
strcpy (child->progname, myself->progname);
/* Restore impersonation */
cygheap->user.reimpersonate ();
@ -478,18 +402,18 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
ProtectHandle (pi.hThread);
/* Protect the handle but name it similarly to the way it will
be called in subproc handling. */
ProtectHandle1 (pi.hProcess, childhProc);
ProtectHandle (pi.hProcess);
/* Fill in fields in the child's process table entry. */
forked->dwProcessId = pi.dwProcessId;
forked.hProcess = pi.hProcess;
child->dwProcessId = pi.dwProcessId;
child.hProcess = pi.hProcess;
/* 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 ())
if (!child.remember ())
{
TerminateProcess (pi.hProcess, 1);
set_errno (EAGAIN);
@ -501,8 +425,11 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
#endif
/* Wait for subproc to initialize itself. */
if (!sync_with_child (pi, subproc_ready, true, "waiting for longjmp"))
goto cleanup;
if (!ch.sync (child, 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)");
@ -547,9 +474,13 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
}
/* 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"))
if (!resume_child (forker_finished))
goto cleanup;
else if (!ch.sync (child, FORK_WAIT_TIMEOUT))
{
system_printf ("child %d died waiting for dll loading", child_pid);
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
@ -567,17 +498,17 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
goto cleanup;
}
/* Start the child up again. */
(void) resume_child (pi, forker_finished);
(void) resume_child (forker_finished);
}
ForceCloseHandle (subproc_ready);
ForceCloseHandle (pi.hProcess);
ForceCloseHandle (pi.hThread);
ForceCloseHandle (forker_finished);
forker_finished = NULL;
pi.hThread = NULL;
pthread::atforkparent ();
return forked_pid;
return child_pid;
/* Common cleanup code for failure cases */
cleanup:
@ -589,8 +520,6 @@ fork_parent (HANDLE& hParent, dll *&first_dll,
ForceCloseHandle1 (pi.hProcess, childhProc);
if (pi.hThread)
ForceCloseHandle (pi.hThread);
if (subproc_ready)
ForceCloseHandle (subproc_ready);
if (forker_finished)
ForceCloseHandle (forker_finished);
return -1;
@ -618,6 +547,11 @@ fork ()
myself->set_has_pgid_children ();
child_info_fork ch;
if (ch.subproc_ready == NULL)
{
system_printf ("unable to allocate subproc_ready event, %E");
return -1;
}
sig_send (NULL, __SIGHOLD);
int res = setjmp (ch.jmp);

View File

@ -87,24 +87,24 @@ unsigned long cygwin_internal (cygwin_getinfo_types, ...);
/* Flags associated with process_state */
enum
{
PID_IN_USE = 0x0001, /* Entry in use. */
PID_ZOMBIE = 0x0002, /* Child exited: no parent wait. */
PID_STOPPED = 0x0004, /* Waiting for SIGCONT. */
PID_TTYIN = 0x0008, /* Waiting for terminal input. */
PID_TTYOU = 0x0010, /* Waiting for terminal output. */
PID_ORPHANED = 0x0020, /* Member of an orphaned process group. */
PID_ACTIVE = 0x0040, /* Pid accepts signals. */
PID_CYGPARENT = 0x0080, /* Set if parent was a cygwin app. */
PID_MAP_RW = 0x0100, /* Flag to open map rw. */
PID_MYSELF = 0x0200, /* Flag that pid is me. */
PID_NOCLDSTOP = 0x0400, /* Set if no SIGCHLD signal on stop. */
PID_INITIALIZING = 0x0800, /* Set until ready to receive signals. */
PID_USETTY = 0x1000, /* Setting this enables or disables cygwin's */
/* tty support. This is inherited by */
/* all execed or forked processes. */
PID_ALLPIDS = 0x2000, /* child has execed */
PID_EXECED = 0x4000, /* redirect to original pid info block */
PID_NOREDIR = 0x8000, /* don't redirect if execed */
PID_IN_USE = 0x00001, /* Entry in use. */
PID_ZOMBIE = 0x00002, /* Child exited: no parent wait. */
PID_STOPPED = 0x00004, /* Waiting for SIGCONT. */
PID_TTYIN = 0x00008, /* Waiting for terminal input. */
PID_TTYOU = 0x00010, /* Waiting for terminal output. */
PID_ORPHANED = 0x00020, /* Member of an orphaned process group. */
PID_ACTIVE = 0x00040, /* Pid accepts signals. */
PID_CYGPARENT = 0x00080, /* Set if parent was a cygwin app. */
PID_MAP_RW = 0x00100, /* Flag to open map rw. */
PID_MYSELF = 0x00200, /* Flag that pid is me. */
PID_NOCLDSTOP = 0x00400, /* Set if no SIGCHLD signal on stop. */
PID_INITIALIZING = 0x00800, /* Set until ready to receive signals. */
PID_USETTY = 0x01000, /* Setting this enables or disables cygwin's
tty support. This is inherited by
all execed or forked processes. */
PID_ALLPIDS = 0x02000, /* used by pinfo scanner */
PID_EXECED = 0x04000, /* redirect to original pid info block */
PID_NOREDIR = 0x08000, /* don't redirect if execed */
PID_EXITED = 0x80000000 /* Free entry. */
};

View File

@ -50,21 +50,25 @@ set_myself (HANDLE h)
cygheap->pid = cygwin_pid (GetCurrentProcessId ());
myself.init (cygheap->pid, PID_IN_USE | PID_MYSELF, h);
myself->process_state |= PID_IN_USE;
myself->start_time = time (NULL); /* Register our starting time. */
myself->dwProcessId = GetCurrentProcessId ();
(void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname));
if (!strace.active)
strace.hello ();
debug_printf ("myself->dwProcessId %u", myself->dwProcessId);
InitializeCriticalSection (&myself.lock);
myself->dwProcessId = GetCurrentProcessId ();
myself.initialize_lock ();
if (h)
{
/* here if execed */
static pinfo NO_COPY myself_identity;
myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED);
myself->start_time = time (NULL); /* Register our starting time. */
myself->exec_sendsig = NULL;
myself->exec_dwProcessId = 0;
}
else if (myself->wr_proc_pipe)
else if (!myself->wr_proc_pipe)
myself->start_time = time (NULL); /* Register our starting time. */
else
{
/* We've inherited the parent's wr_proc_pipe. We don't need it,
so close it. */
@ -522,7 +526,7 @@ _pinfo::commune_send (DWORD code, ...)
__seterrno ();
goto err;
}
EnterCriticalSection (&myself.lock);
myself.lock ();
myself->tothem = tome;
myself->fromthem = fromme;
myself->hello_pid = pid;
@ -626,7 +630,7 @@ err:
out:
myself->hello_pid = 0;
LeaveCriticalSection (&myself.lock);
myself.unlock ();
return res;
}
@ -710,13 +714,6 @@ proc_waiter (void *arg)
/* Child exited. Do some cleanup and signal myself. */
CloseHandle (vchild.rd_proc_pipe);
vchild.rd_proc_pipe = NULL;
if (vchild->process_state != PID_EXITED && vchild.hProcess)
{
DWORD exit_code;
if (GetExitCodeProcess (vchild.hProcess, &exit_code))
vchild->exitcode = (exit_code & 0xff) << 8;
}
if (WIFEXITED (vchild->exitcode))
si.si_sigval.sival_int = CLD_EXITED;
else if (WCOREDUMP (vchild->exitcode))
@ -730,33 +727,13 @@ proc_waiter (void *arg)
case SIGTTOU:
case SIGTSTP:
case SIGSTOP:
if (ISSTATE (myself, PID_NOCLDSTOP)) // FIXME: No need for this flag to be in _pinfo any longer
continue;
/* Child stopped. Signal myself. */
si.si_sigval.sival_int = CLD_STOPPED;
break;
case SIGCONT:
continue;
case __ALERT_REPARENT: /* sigh */
/* spawn_guts has signalled us that it has just started a new
subprocess which will take over this cygwin pid. */
/* We need to keep a handle to the original windows process which
represents the cygwin process around to make sure that the
windows pid is not reused before we are through with it.
So, detect the first time that a subprocess calls exec
and save the current hprocess in the pid_handle field.
On subsequent execs just close the handle. */
if (!vchild.hProcess)
/* something went wrong. oh well. */;
else if (vchild.pid_handle)
ForceCloseHandle1 (vchild.hProcess, childhProc);
else
vchild.pid_handle = vchild.hProcess;
vchild.hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE,
vchild->dwProcessId);
vchild->cygstarted++;
if (vchild.hProcess)
ProtectHandle1 (vchild.hProcess, childhProc);
continue;
default:
system_printf ("unknown value %d on proc pipe", buf);
continue;
@ -790,6 +767,40 @@ proc_waiter (void *arg)
return 0;
}
void
proc_pipe::set (bool closeem)
{
myself.lock ();
if (!CreatePipe (&in, &out, &sec_none_nih, 16))
{
system_printf ("couldn't create pipe, %E");
return;
}
/* Duplicate the write end of the pipe into the subprocess. Make it inheritable
so that all of the execed children get it. */
if (!DuplicateHandle (hMainProc, out, hMainProc, &out, 0, TRUE,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
{
CloseHandle (in);
in = out = NULL;
system_printf ("couldn't make handle %p noninheritable, %E", out);
return;
}
_closeem = closeem;
}
proc_pipe::~proc_pipe ()
{
if (_closeem)
{
if (in)
CloseHandle (in);
if (out)
CloseHandle (out);
}
myself.unlock ();
}
/* function to set up the process pipe and kick off proc_waiter */
int
pinfo::wait ()

View File

@ -111,6 +111,8 @@ public:
/* signals */
HANDLE sendsig;
HANDLE exec_sendsig;
DWORD exec_dwProcessId;
private:
sigset_t sig_mask;
public:
@ -132,14 +134,14 @@ class pinfo
public:
HANDLE rd_proc_pipe;
HANDLE hProcess;
CRITICAL_SECTION lock;
CRITICAL_SECTION _lock;
/* Handle associated with initial Windows pid which started it all. */
HANDLE pid_handle;
void init (pid_t, DWORD, HANDLE = NULL) __attribute__ ((regparm(3)));
pinfo () {}
pinfo (_pinfo *x): procinfo (x), hProcess (NULL), pid_handle (NULL) {}
pinfo (pid_t n) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, 0);}
pinfo (pid_t n, DWORD flag) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, flag);}
pinfo (pid_t n, DWORD flag) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, flag);}
void release ();
int wait () __attribute__ ((regparm (1)));
~pinfo ()
@ -147,7 +149,9 @@ public:
if (destroy && procinfo)
release ();
}
void initialize_lock () {InitializeCriticalSection (&_lock);}
void lock () {EnterCriticalSection (&_lock);}
void unlock () {LeaveCriticalSection (&_lock);}
_pinfo *operator -> () const {return procinfo;}
int operator == (pinfo *x) const {return x->procinfo == procinfo;}
int operator == (pinfo &x) const {return x.procinfo == procinfo;}
@ -175,6 +179,20 @@ public:
void set_acl();
};
class proc_pipe
{
bool _closeem;
public:
HANDLE in;
HANDLE out;
void set (bool);
proc_pipe (bool closeem) {set (closeem);}
proc_pipe () : _closeem (false), in (NULL), out (NULL) {};
void close () {_closeem = true;}
~proc_pipe ();
int operator == (int x) {return (int) in == x;}
};
#define ISSTATE(p, f) (!!((p)->process_state & f))
#define NOTSTATE(p, f) (!((p)->process_state & f))

View File

@ -200,19 +200,18 @@ proc_exists (_pinfo *p)
return p && !(p->process_state & (PID_EXITED | PID_ZOMBIE));
}
/* Return 1 if this is one of our children, zero otherwise.
FIXME: This really should be integrated with the rest of the proc_subproc
testing. Scanning these lists twice is inefficient. */
bool __stdcall
/* Return true if this is one of our children, false otherwise. */
static inline bool __stdcall
mychild (int pid)
{
pinfo p (pid);
return p && p->ppid == myself->pid;
for (int i = 0; i < nprocs; i++)
if (procs[i]->pid == pid)
return true;
return false;
}
/* Handle all subprocess requests
*/
#define vchild (*((pinfo *) val))
int __stdcall
proc_subproc (DWORD what, DWORD val)
{
@ -223,6 +222,7 @@ proc_subproc (DWORD what, DWORD val)
waitq *w;
#define wval ((waitq *) val)
#define vchild (*((pinfo *) val))
sigproc_printf ("args: %x, %d", what, val);
@ -244,18 +244,21 @@ proc_subproc (DWORD what, DWORD val)
sigproc_printf ("proc table overflow: hit %d processes, pid %d\n",
nprocs, vchild->pid);
rc = 0;
set_errno (EMFILE); // FIXMENOW - what's the right errno?
set_errno (EAGAIN);
break;
}
vchild->ppid = myself->pid;
vchild->uid = myself->uid;
vchild->gid = myself->gid;
vchild->pgid = myself->pgid;
vchild->sid = myself->sid;
vchild->ctty = myself->ctty;
vchild->cygstarted = true;
vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY);
if (vchild != myself)
{
vchild->ppid = myself->pid;
vchild->uid = myself->uid;
vchild->gid = myself->gid;
vchild->pgid = myself->pgid;
vchild->sid = myself->sid;
vchild->ctty = myself->ctty;
vchild->cygstarted = true;
vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY);
}
procs[nprocs] = vchild;
rc = procs[nprocs].wait ();
if (rc)
@ -353,6 +356,8 @@ out:
out1:
sigproc_printf ("returning %d", rc);
return rc;
#undef wval
#undef vchild
}
// FIXME: This is inelegant
@ -566,9 +571,27 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
sendsig = myself->sendsig;
else
{
for (int i = 0; !p->dwProcessId && i < 10000; i++)
HANDLE dupsig;
DWORD dwProcessId;
for (int i = 0; !p->sendsig && i < 10000; i++)
low_priority_sleep (0);
HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId);
if (p->sendsig)
{
dupsig = p->sendsig;
dwProcessId = p->dwProcessId;
}
else
{
dupsig = p->exec_sendsig;
dwProcessId = p->exec_dwProcessId;
}
if (!dupsig)
{
set_errno (EAGAIN);
sigproc_printf ("sendsig handle never materialized");
goto out;
}
HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
if (!hp)
{
sigproc_printf ("OpenProcess failed, %E");
@ -576,14 +599,12 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
goto out;
}
VerifyHandle (hp);
for (int i = 0; !p->sendsig && i < 10000; i++)
low_priority_sleep (0);
if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0,
if (!DuplicateHandle (hp, dupsig, hMainProc, &sendsig, false, 0,
DUPLICATE_SAME_ACCESS) || !sendsig)
{
CloseHandle (hp);
sigproc_printf ("DuplicateHandle failed, %E");
__seterrno ();
sigproc_printf ("DuplicateHandle failed, %E");
CloseHandle (hp);
goto out;
}
CloseHandle (hp);
@ -695,17 +716,98 @@ out:
/* Initialize some of the memory block passed to child processes
by fork/spawn/exec. */
void __stdcall
init_child_info (DWORD chtype, child_info *ch, HANDLE subproc_ready)
child_info::child_info (unsigned in_cb, child_info_types chtype)
{
memset (ch, 0, sizeof *ch);
ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info);
ch->intro = PROC_MAGIC_GENERIC;
ch->magic = CHILD_INFO_MAGIC;
ch->type = chtype;
ch->subproc_ready = subproc_ready;
ch->fhandler_union_cb = sizeof (fhandler_union);
ch->user_h = cygwin_user_h;
memset (this, 0, in_cb);
cb = in_cb;
intro = PROC_MAGIC_GENERIC;
magic = CHILD_INFO_MAGIC;
type = chtype;
fhandler_union_cb = sizeof (fhandler_union);
user_h = cygwin_user_h;
if (chtype != PROC_SPAWN)
subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL);
sigproc_printf ("subproc_ready %p", subproc_ready);
if (chtype != PROC_EXEC && myself->wr_proc_pipe != INVALID_HANDLE_VALUE)
parent_wr_proc_pipe = myself->wr_proc_pipe;
}
child_info::~child_info ()
{
if (subproc_ready)
CloseHandle (subproc_ready);
}
child_info_fork::child_info_fork () :
child_info (sizeof *this, _PROC_FORK)
{
}
child_info_spawn::child_info_spawn (child_info_types chtype) :
child_info (sizeof *this, chtype)
{
}
void
child_info::ready (bool execed)
{
if (!subproc_ready)
{
sigproc_printf ("subproc_ready not set");
return;
}
if (!SetEvent (subproc_ready))
api_fatal ("SetEvent failed");
else
sigproc_printf ("signalled %p that I was ready", subproc_ready);
if (execed)
{
CloseHandle (subproc_ready);
subproc_ready = NULL;
}
}
bool
child_info::sync (pinfo& vchild, DWORD howlong)
{
if (!subproc_ready)
{
sigproc_printf ("not waiting. subproc_ready is NULL");
return false;
}
HANDLE w4[2];
w4[0] = subproc_ready;
w4[1] = vchild.hProcess;
bool res;
sigproc_printf ("waiting for subproc_ready(%p) and child process(%p)", w4[0], w4[1]);
switch (WaitForMultipleObjects (2, w4, FALSE, howlong))
{
case WAIT_OBJECT_0:
sigproc_printf ("got subproc_ready for pid %d", vchild->pid);
res = true;
break;
case WAIT_OBJECT_0 + 1:
if (WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0)
sigproc_printf ("should never happen. noticed subproc_ready after process exit");
else
{
DWORD exitcode = 0;
(void) GetExitCodeProcess (vchild.hProcess, &exitcode);
vchild->exitcode = (exitcode & 0xff) << 8;
sigproc_printf ("non-cygwin exit value is %p", exitcode);
}
res = false;
break;
default:
system_printf ("wait failed, pid %d, %E", vchild->pid);
res = false;
break;
}
return res;
}
/* Check the state of all of our children to see if any are stopped or

View File

@ -372,17 +372,15 @@ spawn_guts (const char * prog_arg, const char *const *argv,
STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
child_info_spawn ciresrv;
si.lpReserved2 = (LPBYTE) &ciresrv;
si.cbReserved2 = sizeof (ciresrv);
DWORD chtype;
child_info_types chtype;
if (mode != _P_OVERLAY)
chtype = PROC_SPAWN;
else
chtype = PROC_EXEC;
init_child_info (chtype, &ciresrv, NULL);
child_info_spawn ciresrv (chtype);
si.lpReserved2 = (LPBYTE) &ciresrv;
si.cbReserved2 = sizeof (ciresrv);
ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info));
ciresrv.moreinfo->old_title = NULL;
@ -616,16 +614,21 @@ spawn_guts (const char * prog_arg, const char *const *argv,
if (mode == _P_DETACH || !set_console_state_for_spawn ())
flags |= DETACHED_PROCESS;
HANDLE saved_sendsig;
bool reset_sendsig = false;
if (mode != _P_OVERLAY)
saved_sendsig = NULL;
myself->exec_sendsig = NULL;
else
{
/* Reset sendsig so that any process which wants to send a signal
to this pid will wait for the new process to become active.
Save the old value in case the exec fails. */
saved_sendsig = myself->sendsig;
myself->sendsig = INVALID_HANDLE_VALUE;
if (!myself->exec_sendsig)
{
myself->exec_sendsig = myself->sendsig;
myself->exec_dwProcessId = myself->dwProcessId;
myself->sendsig = NULL;
reset_sendsig = true;
}
/* Save a copy of a handle to the current process around the first time we
exec so that the pid will not be reused. Why did I stop cygwin from
generating its own pids again? */
@ -636,15 +639,13 @@ spawn_guts (const char * prog_arg, const char *const *argv,
ProtectHandle (cygheap->pid_handle);
else
system_printf ("duplicate to pid_handle failed, %E");
ciresrv.parent_wr_proc_pipe = myself->wr_proc_pipe;
}
/* Start the process in a suspended state. Needed so that any potential parent will
be able to take notice of the new "execed" process. This is only really needed
to handle exec'ed windows processes since cygwin processes are smart enough that
the parent doesn't have to bother but what are you gonna do? Cygwin lives in
a windows world. */
if (mode != _P_OVERLAY || !real_path.iscygexec ())
/* 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 (mode != _P_OVERLAY || cygheap->fdtab.need_fixup_before ())
flags |= CREATE_SUSPENDED;
const char *runpath = null_app_name ? NULL : (const char *) real_path;
@ -739,8 +740,11 @@ spawn_guts (const char * prog_arg, const char *const *argv,
__seterrno ();
syscall_printf ("CreateProcess failed, %E");
/* If this was a failed exec, restore the saved sendsig. */
if (saved_sendsig)
myself->sendsig = saved_sendsig;
if (reset_sendsig)
{
myself->sendsig = myself->exec_sendsig;
myself->exec_sendsig = NULL;
}
cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
return -1;
}
@ -780,41 +784,32 @@ spawn_guts (const char * prog_arg, const char *const *argv,
rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf);
/* Name the handle similarly to proc_subproc. */
ProtectHandle1 (pi.hProcess, childhProc);
ProtectHandle (pi.hProcess);
int wait_for_myself = false;
DWORD exec_cygstarted;
bool wait_for_myself = false;
if (mode == _P_OVERLAY)
{
if (!real_path.iscygexec ())
{
/* Store the old exec_cygstarted since this is used as a crude semaphore for
detecting when the parent has noticed the change in windows pid for this
cygwin pid. */
exec_cygstarted = myself->cygstarted;
myself->dwProcessId = dwExeced = pi.dwProcessId; /* Reparenting needs this */
myself.alert_parent (__ALERT_REPARENT);
}
CloseHandle (saved_sendsig);
myself->dwProcessId = dwExeced = pi.dwProcessId;
strace.execing = 1;
hExeced = pi.hProcess;
myself.hProcess = hExeced = pi.hProcess;
strcpy (myself->progname, real_path); // FIXME: race?
sigproc_printf ("new process name %s", myself->progname);
close_all_files ();
/* If wr_proc_pipe is NULL then this process was not started by a cygwin
process. So, we need to wait around until the process we've just "execed"
dies. Use our own wait facility to wait for our own pid to exit (there
is some minor special case code in proc_waiter and friends to accommodate
this). */
/* If wr_proc_pipe doesn't exist then this process was not started by a cygwin
process. So, we need to wait around until the process we've just "execed"
dies. Use our own wait facility to wait for our own pid to exit (there
is some minor special case code in proc_waiter and friends to accommodeate
this). */
if (!myself->wr_proc_pipe)
{
myself.hProcess = pi.hProcess;
myself.remember ();
wait_for_myself = true;
}
{
myself.hProcess = pi.hProcess;
myself.remember ();
wait_for_myself = true;
myself->wr_proc_pipe = INVALID_HANDLE_VALUE;
}
}
else
{
exec_cygstarted = 0;
myself->set_has_pgid_children ();
ProtectHandle (pi.hThread);
pinfo child (cygpid, PID_IN_USE);
@ -830,8 +825,9 @@ spawn_guts (const char * prog_arg, const char *const *argv,
child.hProcess = pi.hProcess;
if (!child.remember ())
{
syscall_printf ("process table full");
set_errno (EAGAIN);
/* FIXME: Child in strange state now. */
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
res = -1;
goto out;
}
@ -844,222 +840,217 @@ spawn_guts (const char * prog_arg, const char *const *argv,
However, we should try to find another way to do this eventually. */
(void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess,
NULL, 0, 0, DUPLICATE_SAME_ACCESS);
child->start_time = time (NULL); /* Register child's starting time. */
}
/* Start the child running */
if (flags & CREATE_SUSPENDED)
ResumeThread (pi.hThread);
ForceCloseHandle (pi.hThread);
// ForceCloseHandle (pi.hProcess); // handled by proc_subproc and friends
/* Start the child running */
if (flags & CREATE_SUSPENDED)
ResumeThread (pi.hThread);
ForceCloseHandle (pi.hThread);
sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
if (wait_for_myself)
waitpid (myself->pid, &res, 0);
else
{
/* Loop, waiting for parent to notice pid change, if exec_cygstarted.
In theory this wait should usually be a no-op. */
if (exec_cygstarted)
while (myself->cygstarted == exec_cygstarted && myself.parent_alive ())
low_priority_sleep (0);
res = 42;
}
if (wait_for_myself)
waitpid (myself->pid, &res, 0);
else
ciresrv.sync (myself, INFINITE);
switch (mode)
{
case _P_OVERLAY:
myself->exit (res, 1);
break;
case _P_WAIT:
case _P_SYSTEM:
if (waitpid (cygpid, (int *) &res, 0) != cygpid)
res = -1;
break;
case _P_DETACH:
res = 0; /* Lose all memory of this child. */
break;
case _P_NOWAIT:
case _P_NOWAITO:
case _P_VFORK:
res = cygpid;
break;
default:
break;
}
ForceCloseHandle (pi.hProcess);
switch (mode)
{
case _P_OVERLAY:
myself->exit (res, 1);
break;
case _P_WAIT:
case _P_SYSTEM:
if (waitpid (cygpid, (int *) &res, 0) != cygpid)
res = -1;
break;
case _P_DETACH:
res = 0; /* Lose all memory of this child. */
break;
case _P_NOWAIT:
case _P_NOWAITO:
case _P_VFORK:
res = cygpid;
break;
default:
break;
}
out:
pthread_cleanup_pop (1);
return (int) res;
pthread_cleanup_pop (1);
return (int) res;
}
extern "C" int
cwait (int *result, int pid, int)
{
return waitpid (pid, result, 0);
return waitpid (pid, result, 0);
}
/*
* Helper function for spawn runtime calls.
* Doesn't search the path.
*/
* Helper function for spawn runtime calls.
* Doesn't search the path.
*/
extern "C" int
spawnve (int mode, const char *path, const char *const *argv,
const char *const *envp)
const char *const *envp)
{
int ret;
int ret;
#ifdef NEWVFORK
vfork_save *vf = vfork_storage.val ();
vfork_save *vf = vfork_storage.val ();
if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
mode = _P_NOWAIT;
else
vf = NULL;
if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
mode = _P_NOWAIT;
else
vf = NULL;
#endif
syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp);
syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp);
switch (mode)
{
case _P_OVERLAY:
/* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
/* Just act as an exec if _P_OVERLAY set. */
spawn_guts (path, argv, envp, mode);
/* Errno should be set by spawn_guts. */
ret = -1;
break;
case _P_VFORK:
case _P_NOWAIT:
case _P_NOWAITO:
case _P_WAIT:
case _P_DETACH:
case _P_SYSTEM:
ret = spawn_guts (path, argv, envp, mode);
switch (mode)
{
case _P_OVERLAY:
/* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
/* Just act as an exec if _P_OVERLAY set. */
spawn_guts (path, argv, envp, mode);
/* Errno should be set by spawn_guts. */
ret = -1;
break;
case _P_VFORK:
case _P_NOWAIT:
case _P_NOWAITO:
case _P_WAIT:
case _P_DETACH:
case _P_SYSTEM:
ret = spawn_guts (path, argv, envp, mode);
#ifdef NEWVFORK
if (vf)
{
if (ret > 0)
{
debug_printf ("longjmping due to vfork");
vf->restore_pid (ret);
}
}
if (vf)
{
if (ret > 0)
{
debug_printf ("longjmping due to vfork");
vf->restore_pid (ret);
}
}
#endif
break;
default:
set_errno (EINVAL);
ret = -1;
break;
}
return ret;
break;
default:
set_errno (EINVAL);
ret = -1;
break;
}
return ret;
}
/*
* spawn functions as implemented in the MS runtime library.
* Most of these based on (and copied from) newlib/libc/posix/execXX.c
*/
* spawn functions as implemented in the MS runtime library.
* Most of these based on (and copied from) newlib/libc/posix/execXX.c
*/
extern "C" int
spawnl (int mode, const char *path, const char *arg0, ...)
{
int i;
va_list args;
const char *argv[256];
int i;
va_list args;
const char *argv[256];
va_start (args, arg0);
argv[0] = arg0;
i = 1;
va_start (args, arg0);
argv[0] = arg0;
i = 1;
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
va_end (args);
va_end (args);
return spawnve (mode, path, (char * const *) argv, cur_environ ());
return spawnve (mode, path, (char * const *) argv, cur_environ ());
}
extern "C" int
spawnle (int mode, const char *path, const char *arg0, ...)
{
int i;
va_list args;
const char * const *envp;
const char *argv[256];
int i;
va_list args;
const char * const *envp;
const char *argv[256];
va_start (args, arg0);
argv[0] = arg0;
i = 1;
va_start (args, arg0);
argv[0] = arg0;
i = 1;
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
envp = va_arg (args, const char * const *);
va_end (args);
envp = va_arg (args, const char * const *);
va_end (args);
return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
}
extern "C" int
spawnlp (int mode, const char *path, const char *arg0, ...)
{
int i;
va_list args;
const char *argv[256];
int i;
va_list args;
const char *argv[256];
va_start (args, arg0);
argv[0] = arg0;
i = 1;
va_start (args, arg0);
argv[0] = arg0;
i = 1;
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
va_end (args);
va_end (args);
return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
}
extern "C" int
spawnlpe (int mode, const char *path, const char *arg0, ...)
{
int i;
va_list args;
const char * const *envp;
const char *argv[256];
int i;
va_list args;
const char * const *envp;
const char *argv[256];
va_start (args, arg0);
argv[0] = arg0;
i = 1;
va_start (args, arg0);
argv[0] = arg0;
i = 1;
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
do
argv[i] = va_arg (args, const char *);
while (argv[i++] != NULL);
envp = va_arg (args, const char * const *);
va_end (args);
envp = va_arg (args, const char * const *);
va_end (args);
return spawnvpe (mode, path, (char * const *) argv, envp);
return spawnvpe (mode, path, (char * const *) argv, envp);
}
extern "C" int
spawnv (int mode, const char *path, const char * const *argv)
{
return spawnve (mode, path, argv, cur_environ ());
return spawnve (mode, path, argv, cur_environ ());
}
extern "C" int
spawnvp (int mode, const char *path, const char * const *argv)
{
return spawnvpe (mode, path, argv, cur_environ ());
return spawnvpe (mode, path, argv, cur_environ ());
}
extern "C" int
spawnvpe (int mode, const char *file, const char * const *argv,
const char * const *envp)
const char * const *envp)
{
path_conv buf;
return spawnve (mode, find_exec (file, buf), argv, envp);
path_conv buf;
return spawnve (mode, find_exec (file, buf), argv, envp);
}