summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/pinfo.cc
diff options
context:
space:
mode:
authorChristopher Faylor <me@cgf.cx>2004-11-26 04:15:10 +0000
committerChristopher Faylor <me@cgf.cx>2004-11-26 04:15:10 +0000
commit8cb359d9470bc114ea767c99ab65bf603903f150 (patch)
tree23617beea2a909e44cc60e7dbb3594494600388f /winsup/cygwin/pinfo.cc
parentc1ab3396dc48a9bf00e7e6235998c437b5754f89 (diff)
downloadcygnal-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.cc257
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 ()
{