diff options
author | Christopher Faylor <me@cgf.cx> | 2004-11-26 04:15:10 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2004-11-26 04:15:10 +0000 |
commit | 8cb359d9470bc114ea767c99ab65bf603903f150 (patch) | |
tree | 23617beea2a909e44cc60e7dbb3594494600388f /winsup/cygwin/pinfo.cc | |
parent | c1ab3396dc48a9bf00e7e6235998c437b5754f89 (diff) | |
download | cygnal-8cb359d9470bc114ea767c99ab65bf603903f150.tar.gz cygnal-8cb359d9470bc114ea767c99ab65bf603903f150.tar.bz2 cygnal-8cb359d9470bc114ea767c99ab65bf603903f150.zip |
* child_info.h (child_info_spawn::hexec_proc): Eliminate.
* dcrt0.cc (dll_crt0_0): Remove hexec_proc stuff.
* fork.cc (fork_child): Remove call to pinfo_fixup_after_fork.
* pinfo.cc (set_myself): Close and zero pid_handle if set.
(pinfo_fixup_after_fork): Delete.
(proc_waiter): Don't close vchild.hProcess here. Do that when we are remove
the vchild from procs. Save hProcess as pid_handle only on first reparent
operation.
(pinfo::wait): Don't set pid_handle here.
(pinfo::alert_parent): Always try to send signal. If unsuccessful then close
and zero wr_proc_pipe.
* pinfo.h (pinfo::pinfo): Make sure that appropriate parts of the class are
zeroed on construction.
(pinfo::alert_parent): Take char argument.
(pinfo_fixup_after_fork): Delete declaration.
(hexec_proc): Ditto.
* sigproc.cc (remove_proc): Close pid_handle and hProcess if appropriate.
* spawn.cc (spawn_guts): Set cygheap->pid_handle on first exec.
* cygheap.h (init_cygheap::pid_handle): New element.
* pinfo.cc (set_myself): Clear previously existing cygheap->pid_handle when a
new process has been started.
(pinfo::wait): Make sure that a handle to the newly forked/spawned process is
kept around so that the pid will not be reused.
* pinfo.h (_pinfo::pid_handle): Move.
(pinfo::pid_handle): to here.
* spawn.cc (spawn_guts): Create a pid_handle in cygheap prior to spawning to
ensure that the pid does not get reused during the lifetime of the "cygwin
pid".
* pinfo.h (pinfo::alert_parent): New function.
* exceptions.cc (sig_handle_tty_stop): Use alert_parent to send "signals" to
parent.
* fork.cc (fork_parent): Don't close pi.hProcess. Let the waiter thread do
that.
* pinfo.cc (proc_waiter): Detect case where process exits without setting the
exit code and use value from GetExitCodeProcess. Reluctantly implement
__SIGREPARENT.
(pinfo::alert_parent): Define.
* sigproc.h (__SIGREPARENT): New enum.
* spawn.cc (spawn_guts): Send reparent signal to parent on exec. Always create
process in suspended state to avoid races. Remove cygthread.h in favor of
cygtls.h throughout since cygtls now includes cygthread.h. Eliminate
ppid_handle usage throughout.
* child_info.h: Regenerate magic number
(child_info): Remove pppid_handle.
* cygthread.h (cygthread::release): New method. Frees thread without waiting.
* cygthread.cc (cygthread::stub): Set _ctinfo in _mytls to point to information
for executing thread. Don't call SetEvent if thread is no longer in use.
(cygthread::simplestub): Ditto.
* cygtls.h (_cygtls::_ctinfo): New element contains pointer to information
about executing cygthread, if any.
* dcrt0.cc: Remove last vestiges of per_thread stuff.
(dll_crt0_0): Ditto. Remove accommodation for ppid_handle.
(do_exit): Remove obsolete reparenting test.
(_exit): Exit with a more SUSv3-like exit value.
* dtable.cc (dtable::stdio_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* exceptions.cc (open_stackdumpfile): Ditto.
(handle_exceptions): Ditto.
(ctrl_c_handler): Ditto.
(sig_handle_tty_stop): Ditto. Let parent send signal to itself on STOP.
(sigpacket::process): Comment out vfork test.
(signal_exit): Use more SUSv3-like exit value on signal.
* external.cc (fillout_pinfo): Don't set hProcess.
* fork.cc: Remove VFORK cruft.
(per_thread::set): Delete.
(fork_child): Remove perthread stuff.
(fork_parent): Remove obsolete subproc_init. Accommodate new method for
tracking subprocesses.
* pinfo.cc (set_myself): Accommodate new pinfo/_pinfo layout. Set some things
here that used to be set in wait_sig.
(_pinfo::exit): Set exitcode here. Close process pipe.
(_pinfo::commune_send): Accommodeate new pinfo/_pinfo layout.
(proc_waiter): New function. Waits, in a thread for subprocess to go away.
(pinfo::wait): New function. Initialization for proc_waiter.
* pinfo.h (_pinfo::exitcode): New element.
(_pinfo::cygstarted): Ditto.
(_pinfo::wr_proc_pipe): Ditto.
(_pinfo::ppid_handle): Delete.
(_pinfo::hProcess): Delete.
(_pinfo::lock): Delete.
(pinfo::hProcess): New element.
(pinfo::lock): Ditto.
(pinfo::wait): Declare new function.
(pinfo::preserve): Define new function.
* sigproc.cc: Remove old stuff from wait_subproc thread based method.
(zombies): Remove.
(procs): New.
(my_parent_is_alive): Just check that the parent pid exists.
(mychild): Just use pinfo methods to determine if child is mine.
(proc_subproc): Revamp PROC_ADDCHILD to use pinfo::wait. Remove
PROC_CHILDTERMINATED logic. Use different method to remove processes from list
when SIGCHLD == SIG_IGN.
(proc_terminate): Gut.
(subproc_init): Delete.
(init_child_info): Remove setting of pppid_handle.
(checkstate): Revamp to only scan procs array.
(remove_proc): Rename from remove_zombie. Don't close hProcess or pid_handle.
Don't release memory if it's myself.
(stopped_or_terminated): Change logic to handle new consolidated proc/zombie
array.
(wait_subproc): Delete.
* sigproc.h: Remove obsolete EXIT_* defines.
(subproc_init): Remove declaration.
* spawn.cc (spawn_guts): Remove reparenting stuff. Use standard wait logic to
wait for child if started from a non-cygwin process.
* tlsoffsets.h: Regenerate.
* tty.cc (tty_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* include/sys/signal.h (external_pinfo::exitcode): Replace hProcess.
* include/sys/wait.h (WCOREDUMP): Define.
* fhandler_tty.cc (fhandler_tty_slave::read): Add debugging output for timeout
case.
* signal.cc (abort): Flag that we are exiting with the ABORT signal.
Diffstat (limited to 'winsup/cygwin/pinfo.cc')
-rw-r--r-- | winsup/cygwin/pinfo.cc | 257 |
1 files changed, 232 insertions, 25 deletions
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 829db2ad4..8a42daa47 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -24,9 +24,9 @@ details. */ #include "perprocess.h" #include "environ.h" #include <assert.h> +#include <sys/wait.h> #include <ntdef.h> #include "ntdll.h" -#include "cygthread.h" #include "shared_info.h" #include "cygheap.h" #include "fhandler.h" @@ -37,23 +37,6 @@ static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0}; pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks -HANDLE hexec_proc; - -void __stdcall -pinfo_fixup_after_fork () -{ - if (hexec_proc) - CloseHandle (hexec_proc); - /* Keeps the cygpid from being reused. No rights required */ - if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0, - TRUE, 0)) - { - system_printf ("couldn't save current process handle %p, %E", hMainProc); - hexec_proc = NULL; - } - VerifyHandle (hexec_proc); -} - /* Initialize the process table. This is done once when the dll is first loaded. */ @@ -70,7 +53,30 @@ set_myself (HANDLE h) if (!strace.active) strace.hello (); debug_printf ("myself->dwProcessId %u", myself->dwProcessId); - InitializeCriticalSection (&myself->lock); + InitializeCriticalSection (&myself.lock); + myself->dwProcessId = GetCurrentProcessId (); + if (h) + { + /* here if execed */ + static pinfo NO_COPY myself_identity; + myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED); + } + else if (myself->ppid) + { + /* here if forked/spawned */ + pinfo parent (myself->ppid); + /* We've inherited the parent's wr_proc_pipe. We don't need it, + so close it. This could cause problems for the spawn case since there + is no guarantee that a parent will still be around by the time we get + here. If so, we would have a handle leak. FIXME? */ + if (parent && parent->wr_proc_pipe) + CloseHandle (parent->wr_proc_pipe); + if (cygheap->pid_handle) + { + ForceCloseHandle (cygheap->pid_handle); + cygheap->pid_handle = NULL; + } + } return; } @@ -107,17 +113,27 @@ _pinfo::exit (UINT n, bool norecord) exit_state = ES_FINAL; cygthread::terminate (); if (norecord) - sigproc_terminate (); + sigproc_terminate (); /* Just terminate signal and process stuff */ + else + exitcode = n; /* We're really exiting. Record the UNIX exit code. */ + if (this) { - if (!norecord) - process_state = PID_EXITED; - /* FIXME: There is a potential race between an execed process and its parent here. I hated to add a mutex just for this, though. */ struct rusage r; fill_rusage (&r, hMainProc); add_rusage (&rusage_self, &r); + + if (!norecord) + { + process_state = PID_EXITED; + /* We could just let this happen automatically when the process + exits but this should gain us a microsecond or so by notifying + the parent early. */ + if (wr_proc_pipe) + CloseHandle (wr_proc_pipe); + } } sigproc_printf ("Calling ExitProcess %d", n); @@ -259,6 +275,7 @@ pinfo::init (pid_t n, DWORD flag, HANDLE in_h) procinfo->process_state |= PID_IN_USE | PID_EXECED; procinfo->pid = myself->pid; } + break; } destroy = 1; @@ -505,7 +522,7 @@ _pinfo::commune_send (DWORD code, ...) __seterrno (); goto err; } - EnterCriticalSection (&myself->lock); + EnterCriticalSection (&myself.lock); myself->tothem = tome; myself->fromthem = fromme; myself->hello_pid = pid; @@ -609,7 +626,7 @@ err: out: myself->hello_pid = 0; - LeaveCriticalSection (&myself->lock); + LeaveCriticalSection (&myself.lock); return res; } @@ -642,6 +659,196 @@ _pinfo::cmdline (size_t& n) return s; } +/* This is the workhorse which waits for the write end of the pipe + created during new process creation. If the pipe is closed, it is + assumed that the cygwin pid has exited. Otherwise, various "signals" + can be sent to the parent to inform the parent to perform a certain + action. + + This code was originally written to eliminate the need for "reparenting" + but, unfortunately, reparenting is still needed in order to get the + exit code of an execed windows process. Otherwise, the exit code of + a cygwin process comes from the exitcode field in _pinfo. */ +static DWORD WINAPI +proc_waiter (void *arg) +{ + extern HANDLE hExeced; + pinfo& vchild = *(pinfo *) arg; + + siginfo_t si; + si.si_signo = SIGCHLD; + si.si_code = SI_KERNEL; + si.si_pid = vchild->pid; + si.si_errno = 0; +#if 0 // FIXME: This is tricky to get right + si.si_utime = pchildren[rc]->rusage_self.ru_utime; + si.si_stime = pchildren[rc].rusage_self.ru_stime; +#else + si.si_utime = 0; + si.si_stime = 0; +#endif + pid_t pid = vchild->pid; + + for (;;) + { + DWORD nb; + char buf = '\0'; + if (!ReadFile (vchild.rd_proc_pipe, &buf, 1, &nb, NULL) + && GetLastError () != ERROR_BROKEN_PIPE) + { + system_printf ("error on read of child wait pipe %p, %E", vchild.rd_proc_pipe); + break; + } + + si.si_uid = vchild->uid; + + switch (buf) + { + case 0: + /* 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)) + si.si_sigval.sival_int = CLD_DUMPED; + else + si.si_sigval.sival_int = CLD_KILLED; + si.si_status = vchild->exitcode; + vchild->process_state = PID_ZOMBIE; + break; + case SIGTTIN: + case SIGTTOU: + case SIGTSTP: + case SIGSTOP: + /* Child stopped. Signal myself. */ + si.si_sigval.sival_int = CLD_STOPPED; + break; + case SIGCONT: + continue; + case __SIGREPARENT: /* 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; + } + + /* Special case: If the "child process" that died is us, then we're + execing. Just call proc_subproc directly and then exit this loop. + We're done here. */ + if (hExeced && vchild->pid == myself->pid) + { + /* execing. no signals available now. */ + proc_subproc (PROC_CLEARWAIT, 0); + break; + } + + /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc + to avoid the proc_subproc lock since the signal thread will eventually + be calling proc_subproc and could unnecessarily block. */ + sig_send (myself_nowait, si); + + /* If we're just stopped or got a continue signal, keep looping. + Otherwise, return this thread to the pool. */ + if (buf != '\0') + sigproc_printf ("looping"); + else + break; + } + + sigproc_printf ("exiting wait thread for pid %d", pid); + _my_tls._ctinfo->release (); /* return the cygthread to the cygthread pool */ + return 0; +} + +/* function to set up the process pipe and kick off proc_waiter */ +int +pinfo::wait () +{ + HANDLE out; + /* FIXME: execed processes should be able to wait for pids that were started + by the process which execed them. */ + if (!CreatePipe (&rd_proc_pipe, &out, &sec_none_nih, 16)) + { + system_printf ("Couldn't create pipe tracker for pid %d, %E", + (*this)->pid); + return 0; + } + /* 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, hProcess, &((*this)->wr_proc_pipe), 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + system_printf ("Couldn't duplicate pipe topid %d(%p), %E", (*this)->pid, + hProcess); + return 0; + } + CloseHandle (out); /* Don't need this end in this proces */ + + preserve (); /* Preserve the shared memory associated with the pinfo */ + + /* Fire up a new thread to track the subprocess */ + cygthread *h = new cygthread (proc_waiter, this, "sig"); + if (!h) + sigproc_printf ("tracking thread creation failed for pid %d", (*this)->pid); + else + { + h->zap_h (); + sigproc_printf ("created tracking thread for pid %d, winpid %p, rd_pipe %p", + (*this)->pid, (*this)->dwProcessId, rd_proc_pipe); + } + + return 1; +} + +/* function to send a "signal" to the parent when something interesting happens + in the child. */ +void +pinfo::alert_parent (char sig) +{ + DWORD nb; + /* Send something to our parent. If the parent has gone away, + close the pipe. */ + if (myself->wr_proc_pipe + && WriteFile (myself->wr_proc_pipe, &sig, 1, &nb, NULL)) + /* all is well */; + else if (GetLastError () != ERROR_BROKEN_PIPE) + debug_printf ("sending %d notification to parent failed, %E", sig); + else + { + HANDLE closeit = myself->wr_proc_pipe; + myself->wr_proc_pipe = NULL; + CloseHandle (closeit); + } +} + void pinfo::release () { |