Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/sigproc.cc')
-rw-r--r--winsup/cygwin/sigproc.cc1345
1 files changed, 1345 insertions, 0 deletions
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
new file mode 100644
index 000000000..97a0f3a35
--- /dev/null
+++ b/winsup/cygwin/sigproc.cc
@@ -0,0 +1,1345 @@
+/* sigproc.cc: inter/intra signal and sub process handler
+
+ Copyright 1997, 1998, 1999, 2000 Cygnus Solutions.
+
+ Written by Christopher Faylor <cgf@cygnus.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <stdlib.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "winsup.h"
+
+extern BOOL allow_ntsec;
+
+/*
+ * Convenience defines
+ */
+#define WSSC 60000 // Wait for signal completion
+#define WPSP 40000 // Wait for proc_subproc mutex
+#define WSPX 20000 // Wait for wait_sig to terminate
+#define WWSP 20000 // Wait for wait_subproc to terminate
+
+#define WAIT_SIG_PRIORITY THREAD_PRIORITY_HIGHEST
+
+#define TOTSIGS (NSIG + __SIGOFFSET)
+
+#define sip_printf(fmt, args...) sigproc_printf (fmt , ## args)
+
+#define wake_wait_subproc() SetEvent (events[0])
+
+#define no_signals_available() (!hwait_sig || !sig_loop_wait)
+
+/*
+ * Global variables
+ */
+const char *__sp_fn ;
+int __sp_ln;
+
+char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to
+ // current process but no wait is required
+char NO_COPY myself_nowait_nonmain_dummy[1] = {'1'};// Flag to sig_send that signal goes to
+ // current process but no wait is required
+ // if this is not the main thread.
+
+HANDLE NO_COPY signal_arrived; // Event signaled when a signal has
+ // resulted in a user-specified
+ // function call
+/*
+ * Common variables
+ */
+
+
+/* How long to wait for message/signals. Normally this is infinite.
+ * On termination, however, these are set to zero as a flag to exit.
+ */
+
+#define Static static NO_COPY
+
+Static DWORD proc_loop_wait = 500; // Wait for subprocesses to exit
+Static DWORD sig_loop_wait = 500; // Wait for signals to arrive
+
+Static HANDLE sigcatch_nonmain = NULL; // The semaphore signaled when
+ // signals are available for
+ // processing from non-main thread
+Static HANDLE sigcatch_main = NULL; // Signalled when main thread sends a
+ // signal
+Static HANDLE sigcatch_nosync = NULL; // Signal wait_sig to scan sigtodo
+ // but not to bother with any
+ // synchronization
+Static HANDLE sigcomplete_main = NULL; // Event signaled when a signal has
+ // finished processing for the main
+ // thread
+Static HANDLE sigcomplete_nonmain = NULL;// Semaphore raised for non-main
+ // threads when a signal has finished
+ // processing
+Static HANDLE hwait_sig = NULL; // Handle of wait_sig thread
+Static HANDLE hwait_subproc = NULL; // Handle of sig_subproc thread
+
+Static HANDLE wait_sig_inited = NULL; // Control synchronization of
+ // message queue startup
+Static muto *sync_proc_subproc = NULL; // Control access to
+ // subproc stuff
+
+/* Used by WaitForMultipleObjects. These are handles to child processes.
+ */
+Static HANDLE events[PSIZE + 1] = {0}; // All my children's handles++
+#define hchildren (events + 1) // Where the children handles begin
+Static pinfo *pchildren[PSIZE] = {NULL};// All my children info
+Static pinfo *zombies[PSIZE] = {NULL}; // All my deceased children info
+Static int nchildren = 0; // Number of active children
+Static int nzombies = 0; // Number of deceased children
+
+Static waitq waitq_head = {0}; // Start of queue for wait'ing threads
+Static waitq waitq_main; // Storage for main thread
+
+DWORD NO_COPY maintid = 0; // ID of the main thread
+Static DWORD sigtid = 0; // ID of the signal thread
+
+int NO_COPY pending_signals = 0; // TRUE if signals pending
+
+/* Functions
+ */
+static int __stdcall checkstate (waitq *);
+static BOOL __inline get_proc_lock (DWORD, DWORD);
+static HANDLE __stdcall getsem (pinfo *, const char *, int, int);
+static void __stdcall remove_child (int);
+static void __stdcall remove_zombie (int);
+static DWORD WINAPI wait_sig (VOID *arg);
+static int __stdcall stopped_or_terminated (waitq *, pinfo *);
+static DWORD WINAPI wait_subproc (VOID *);
+
+/* Determine if the parent process is alive.
+ */
+
+BOOL __stdcall
+my_parent_is_alive ()
+{
+ DWORD res;
+ if (!parent_alive)
+ {
+ debug_printf ("No parent_alive mutex");
+ res = FALSE;
+ }
+ else
+ for (int i = 0; i < 2; i++)
+ switch (res = WaitForSingleObject (parent_alive, 0))
+ {
+ case WAIT_OBJECT_0:
+ debug_printf ("parent dead.");
+ res = FALSE;
+ goto out;
+ case WAIT_TIMEOUT:
+ debug_printf ("parent still alive");
+ res = TRUE;
+ goto out;
+ case WAIT_FAILED:
+ DWORD werr = GetLastError ();
+ if (werr == ERROR_INVALID_HANDLE && i == 0)
+ continue;
+ system_printf ("WFSO for parent_alive(%p) failed, error %d",
+ parent_alive, werr);
+ res = FALSE;
+ goto out;
+ }
+out:
+ return res;
+}
+
+__inline static void
+wait_for_me ()
+{
+ /* See if this is the first signal call after initialization.
+ * If so, wait for notification that all initialization has completed.
+ * Then set the handle to NULL to avoid checking this again.
+ */
+ if (wait_sig_inited)
+ {
+ (void) WaitForSingleObject (wait_sig_inited, INFINITE);
+ (void) ForceCloseHandle (wait_sig_inited);
+ wait_sig_inited = NULL;
+ }
+}
+
+static BOOL __stdcall
+proc_can_be_signalled (pinfo *p)
+{
+ if (p == myself_nowait || p == myself_nowait_nonmain || p == myself)
+ {
+ wait_for_me ();
+ return 1;
+ }
+
+ return ISSTATE (p, PID_INITIALIZING) ||
+ (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) ==
+ (PID_ACTIVE | PID_IN_USE));
+}
+
+/* Test to determine if a process really exists and is processing
+ * signals.
+ */
+BOOL __stdcall
+proc_exists (pinfo *p)
+{
+ HANDLE h;
+
+ if (p == NULL)
+ return FALSE;
+
+ if (p == myself || p == myself_nowait_nonmain || p == myself_nowait)
+ return TRUE;
+
+ if (p->process_state == PID_NOT_IN_USE || !p->dwProcessId)
+ return FALSE;
+
+ sip_printf ("checking for existence of pid %d, window pid %d", p->pid,
+ p->dwProcessId);
+ if (p->ppid == myself->pid && p->hProcess != NULL)
+ {
+ sip_printf ("it's mine, process_state %x", p->process_state);
+ return proc_can_be_signalled (p);
+ }
+
+ /* Note: Process is alive if OpenProcess() call fails due to permissions */
+ if (((h = OpenProcess (STANDARD_RIGHTS_REQUIRED, FALSE, p->dwProcessId))
+ != NULL) || (GetLastError () == ERROR_ACCESS_DENIED))
+ {
+ sip_printf ("it exists, %p", h);
+ if (h)
+ {
+ DWORD rc = WaitForSingleObject (h, 0);
+ CloseHandle (h);
+ if (rc == WAIT_OBJECT_0)
+ return 0;
+ }
+ return proc_can_be_signalled (p);
+ }
+
+ sip_printf ("it doesn't exist");
+ /* If the parent pid does not exist, clean this process out of the pinfo
+ * table. It must have died abnormally.
+ */
+ if ((p->pid == p->ppid) || (p->ppid == 1) || !proc_exists (procinfo (p->ppid)))
+ {
+ p->hProcess = NULL;
+ p->process_state = PID_NOT_IN_USE;
+ }
+ return FALSE;
+}
+
+/* Handle all subprocess requests
+ */
+#define vchild ((pinfo *) val)
+int __stdcall
+proc_subproc (DWORD what, DWORD val)
+{
+ int rc = 1;
+ int potential_match;
+ DWORD exitcode;
+ pinfo *child;
+ int send_sigchld = 0;
+ waitq *w;
+
+#define wval ((waitq *) val)
+
+ sip_printf ("args: %x, %d", what, val);
+
+ if (!get_proc_lock (what, val)) // Serialize access to this function
+ {
+ sip_printf ("I am not ready");
+ goto out1;
+ }
+
+ switch (what)
+ {
+ /* Add a new subprocess to the children arrays.
+ * (usually called from the main thread)
+ */
+ case PROC_ADDCHILD:
+ if (nchildren >= PSIZE - 1)
+ system_printf ("nchildren too large %d", nchildren);
+ if (WaitForSingleObject (vchild->hProcess, 0) != WAIT_TIMEOUT)
+ {
+ system_printf ("invalid process handle %p. pid %d, win pid %d",
+ vchild->hProcess, vchild->pid, vchild->dwProcessId);
+ rc = 0;
+ break;
+ }
+
+ pchildren[nchildren] = vchild;
+ hchildren[nchildren] = vchild->hProcess;
+ ProtectHandle (vchild->hProcess);
+ sip_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p",
+ vchild->pid, nchildren, vchild->dwProcessId,
+ vchild->hProcess);
+
+ nchildren++;
+ wake_wait_subproc ();
+ break;
+
+ /* A child is in the stopped state. Scan wait() queue to see if anyone
+ * should be notified. (Called from wait_sig thread)
+ */
+ case PROC_CHILDSTOPPED:
+ child = myself; // Just to avoid accidental NULL dereference
+ sip_printf ("Received stopped notification");
+ goto scan_wait;
+
+ /* A child process had terminated.
+ * Possibly this is just due to an exec(). Cygwin implements an exec()
+ * as a "handoff" from one windows process to another. If child->hProcess
+ * is different from what is recorded in hchildren, then this is an exec().
+ * Otherwise this is a normal child termination event.
+ * (called from wait_subproc thread)
+ */
+ case PROC_CHILDTERMINATED:
+ rc = 0;
+ child = pchildren[val];
+ if (GetExitCodeProcess (hchildren[val], &exitcode) &&
+ hchildren[val] != child->hProcess)
+ {
+ sip_printf ("pid %d[%d], reparented old hProcess %p, new %p",
+ child->pid, val, hchildren[val], child->hProcess);
+ ForceCloseHandle1 (hchildren[val], childhProc);
+ hchildren[val] = child->hProcess; /* Filled out by child */
+ ProtectHandle1 (child->hProcess, childhProc);
+ wake_wait_subproc ();
+ break; // This was an exec()
+ }
+
+ sip_printf ("pid %d[%d] terminated, handle %p, nchildren %d, nzombies %d",
+ child->pid, val, hchildren[val], nchildren, nzombies);
+ remove_child (val); // Remove from children arrays
+ zombies[nzombies++] = child; // Add to zombie array
+ wake_wait_subproc (); // Notify wait_subproc thread that
+ // nchildren has changed.
+ child->process_state = PID_ZOMBIE;// Walking dead
+ if (!proc_loop_wait) // Don't bother if wait_subproc is
+ break; // exiting
+
+ send_sigchld = 1;
+
+ scan_wait:
+ /* Scan the linked list of wait()ing threads. If a wait's parameters
+ * match this pid, then activate it.
+ */
+ for (w = &waitq_head; w->next != NULL; w = w->next)
+ {
+ if ((potential_match = checkstate (w)) > 0)
+ sip_printf ("released waiting thread");
+ else if (potential_match < 0)
+ sip_printf ("only found non-terminated children");
+ else if (potential_match == 0) // nothing matched
+ {
+ sip_printf ("waiting thread found no children");
+ HANDLE oldw = w->next->ev;
+ w->next->ev = NULL;
+ if (!SetEvent (oldw))
+ system_printf ("couldn't wake up wait event %p, %E", oldw);
+ w->next = w->next->next;
+ }
+ if (w->next == NULL)
+ break;
+ }
+
+ sip_printf ("finished processing terminated/stopped child");
+ if (!send_sigchld)
+ break; // No need to send a SIGCHLD
+
+ /* Send a SIGCHLD to myself. */
+ sync_proc_subproc->release (); // Avoid a potential deadlock
+ rc = sig_send (NULL, SIGCHLD); // Send a SIGCHLD
+ goto out1; // Don't try to unlock. We don't have a lock.
+
+
+ /* Clear all waiting threads. Called from exceptions.cc prior to
+ * the main thread's dispatch to a signal handler function.
+ * (called from wait_sig thread)
+ */
+ case PROC_CLEARWAIT:
+ /* Clear all "wait"ing threads. */
+ sip_printf ("clear waiting threads");
+ for (w = &waitq_head; w->next != NULL; w = w->next)
+ {
+ sip_printf ("clearing waiting thread, pid %d", w->next->pid);
+ w->next->status = -1; /* flag that a signal was received */
+ if (!SetEvent (w->next->ev))
+ system_printf ("Couldn't wake up wait event, %E");
+ }
+ waitq_head.next = NULL;
+ sip_printf ("finished clearing");
+ break;
+
+ /* Handle a wait4() operation. Allocates an event for the calling
+ * thread which is signaled when the appropriate pid exits or stops.
+ * (usually called from the main thread)
+ */
+ case PROC_WAIT:
+ wval->ev = NULL; // Don't know event flag yet
+
+ if (wval->pid <= 0)
+ child = NULL; // Not looking for a specific pid
+ else if ((child = procinfo (wval->pid)) == NULL)
+ goto out; // invalid pid. flag no such child
+
+ wval->status = 0; // Don't know status yet
+
+ /* Put waitq structure at the end of a linked list. */
+ for (w = &waitq_head; w->next != NULL; w = w->next)
+ if (w->next == wval && (w->next = w->next->next) == NULL)
+ break;
+
+ wval->next = NULL; /* This will be last in the list */
+ sip_printf ("wval->pid %d, wval->options %d", wval->pid, wval->options);
+
+ /* If the first time for this thread, create a new event, otherwise
+ * reset the event.
+ */
+ if ((wval->ev = wval->thread_ev) == NULL)
+ {
+ wval->ev = wval->thread_ev = CreateEvent (&sec_none_nih, TRUE,
+ FALSE, NULL);
+ ProtectHandle (wval->ev);
+ }
+ ResetEvent (wval->ev);
+
+ /* Scan list of children to see if any have died.
+ * If so, the event flag is set so that the wait* ()
+ * process will return immediately.
+ *
+ * If no children were found and the wait option was WNOHANG,
+ * then set the pid to 0 and remove the waitq value from
+ * consideration.
+ */
+ w->next = wval; /* set at end of wait queue */
+ if ((potential_match = checkstate (w)) <= 0)
+ {
+ if (!potential_match)
+ {
+ w->next = NULL; // don't want to keep looking
+ wval->ev = NULL; // flag that there are no children
+ sip_printf ("no appropriate children, %p, %p",
+ wval->thread_ev, wval->ev);
+ }
+ else if (wval->options & WNOHANG)
+ {
+ w->next = NULL; // don't want to keep looking
+ wval->pid = 0; // didn't find a pid
+ if (!SetEvent (wval->ev)) // wake up wait4 () immediately
+ system_printf ("Couldn't wake up wait event, %E");
+ sip_printf ("WNOHANG and no terminated children, %p, %p",
+ wval->thread_ev, wval->ev);
+ }
+ }
+ if (w->next != NULL)
+ sip_printf ("wait activated %p, %p", wval->thread_ev, wval->ev);
+ else if (wval->ev != NULL)
+ sip_printf ("wait activated %p. Reaped zombie.", wval->ev);
+ else
+ sip_printf ("wait not activated %p, %p", wval->thread_ev, wval->ev);
+ break;
+ }
+
+out:
+ sync_proc_subproc->release (); // Release the lock
+out1:
+ sip_printf ("returning %d", rc);
+ return rc;
+}
+
+/* Terminate the wait_subproc thread.
+ * Called on process exit.
+ * Also called by spawn_guts to disassociate any subprocesses from this
+ * process. Subprocesses will then know to clean up after themselves and
+ * will not become zombies.
+ */
+void __stdcall
+proc_terminate (void)
+{
+ sip_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+ /* Signal processing is assumed to be blocked in this routine. */
+ if (hwait_subproc)
+ {
+ int rc;
+ proc_loop_wait = 0; // Tell wait_subproc thread to exit
+ wake_wait_subproc (); // Wake wait_subproc loop
+
+ /* Wait for wait_subproc thread to exit (but not *too* long) */
+ if ((rc = WaitForSingleObject (hwait_subproc, WWSP)) != WAIT_OBJECT_0)
+ if (rc == WAIT_TIMEOUT)
+ system_printf ("WFSO(hwait_subproc) timed out");
+ else
+ system_printf ("WFSO(hwait_subproc), rc %d, %E", rc);
+
+ HANDLE h = hwait_subproc;
+ hwait_subproc = NULL;
+ ForceCloseHandle1 (h, hwait_subproc);
+
+ sync_proc_subproc->acquire(WPSP);
+ (void) proc_subproc (PROC_CLEARWAIT, 0);
+
+ lock_pinfo_for_update (INFINITE);
+ /* Clean out zombie processes from the pid list. */
+ int i;
+ for (i = 0; i < nzombies; i++)
+ {
+ pinfo *child;
+ if ((child = zombies[i])->hProcess)
+ {
+ ForceCloseHandle1 (child->hProcess, childhProc);
+ child->hProcess = NULL;
+ }
+ child->process_state = PID_NOT_IN_USE;
+ }
+
+ /* Disassociate my subprocesses */
+ for (i = 0; i < nchildren; i++)
+ {
+ pinfo *child;
+ if ((child = pchildren[i])->process_state == PID_NOT_IN_USE)
+ continue; // Should never happen
+ if (!child->hProcess)
+ sip_printf ("%d(%d) hProcess cleared already?", child->pid,
+ child->dwProcessId);
+ else
+ {
+ ForceCloseHandle1 (child->hProcess, childhProc);
+ child->hProcess = NULL;
+ if (!proc_exists (child))
+ {
+ sip_printf ("%d(%d) doesn't exist", child->pid,
+ child->dwProcessId);
+ child->process_state = PID_NOT_IN_USE; /* a reaped child */
+ }
+ else
+ {
+ sip_printf ("%d(%d) closing active child handle", child->pid,
+ child->dwProcessId);
+ child->ppid = 1;
+ if (child->pgid == myself->pid)
+ child->process_state |= PID_ORPHANED;
+ }
+ }
+ }
+ unlock_pinfo ();
+ nchildren = nzombies = 0;
+
+ /* Attempt to close and release sync_proc_subproc in a
+ * non-raceable manner.
+ */
+ muto *m = sync_proc_subproc;
+ sync_proc_subproc = NULL;
+ delete m;
+ }
+ sip_printf ("leaving");
+}
+
+/* Clear pending signal from the sigtodo array
+ */
+void __stdcall
+sig_clear (int sig)
+{
+ (void) InterlockedExchange (myself->getsigtodo(sig), 0L);
+ return;
+}
+
+/* Force the wait_sig thread to wake up and scan the sigtodo array.
+ */
+extern "C" int __stdcall
+sig_dispatch_pending (int force)
+{
+ if (!hwait_sig)
+ return 0;
+
+ int was_pending = pending_signals;
+#ifdef DEBUGGING
+ sip_printf ("pending_signals %d", was_pending);
+#endif
+ if (!was_pending && !force)
+#ifdef DEBUGGING
+ sip_printf ("no need to wake anything up");
+#else
+ ;
+#endif
+ else
+ {
+ wait_for_me ();
+ if (ReleaseSemaphore (sigcatch_nosync, 1, NULL))
+#ifdef DEBUGGING
+ sip_printf ("woke up wait_sig");
+#else
+ ;
+#endif
+ else if (no_signals_available ())
+ /*sip_printf ("I'm going away now")*/;
+ else
+ system_printf ("%E releasing sigcatch_nosync(%p)", sigcatch_nosync);
+ }
+ return was_pending;
+}
+
+/* Message initialization. Called from dll_crt0_1
+ *
+ * This routine starts the signal handling thread. The wait_sig_inited
+ * event is used to signal that the thread is ready to handle signals.
+ * We don't wait for this during initialization but instead detect it
+ * in sig_send to gain a little concurrency.
+ */
+void __stdcall
+sigproc_init ()
+{
+ wait_sig_inited = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ ProtectHandle (wait_sig_inited);
+
+ /* local event signaled when main thread has been dispatched
+ to a signal handler function. */
+ signal_arrived = CreateEvent(&sec_none_nih, TRUE, FALSE, NULL);
+
+ maintid = GetCurrentThreadId ();// For use in determining if signals
+ // should be blocked.
+
+ if (!(hwait_sig = makethread (wait_sig, NULL, 0, "sig")))
+ {
+ system_printf ("cannot create wait_sig thread, %E");
+ api_fatal ("terminating");
+ }
+
+ ProtectHandle (hwait_sig);
+
+ /* sync_proc_subproc is used by proc_subproc. It serialises
+ * access to the children and zombie arrays.
+ */
+ sync_proc_subproc = new_muto (FALSE, NULL);
+
+ /* Initialize waitq structure for main thread. A waitq structure is
+ * allocated for each thread that executes a wait to allow multiple threads
+ * to perform waits. Pre-allocate a waitq structure for the main thread.
+ */
+ waitq *w;
+ if ((w = (waitq *)waitq_storage.get ()) == NULL)
+ {
+ w = &waitq_main;
+ waitq_storage.set (w);
+ }
+ memset (w, 0, sizeof *w); // Just to be safe
+
+ sip_printf ("process/signal handling enabled(%x)", myself->process_state);
+ return;
+}
+
+/* Called on process termination to terminate signal and process threads.
+ */
+void __stdcall
+sigproc_terminate (void)
+{
+ HANDLE h = hwait_sig;
+ hwait_sig = NULL;
+
+ if (GetCurrentThreadId () == sigtid)
+ {
+ ForceCloseHandle (sigcomplete_main);
+ for (int i = 0; i < 20; i++)
+ (void) ReleaseSemaphore (sigcomplete_nonmain, 1, NULL);
+ ForceCloseHandle (sigcomplete_nonmain);
+ ForceCloseHandle (sigcatch_main);
+ ForceCloseHandle (sigcatch_nonmain);
+ ForceCloseHandle (sigcatch_nosync);
+ }
+ proc_terminate (); // Terminate process handling thread
+
+ if (!sig_loop_wait)
+ sip_printf ("sigproc_terminate: sigproc handling not active");
+ else
+ {
+ sigproc_printf ("entering");
+ sig_loop_wait = 0; // Tell wait_sig to exit when it is
+ // finished with anything it is doing
+ sig_dispatch_pending (TRUE); // wake up and die
+
+ /* If !hwait_sig, then the process probably hasn't even finished
+ * its initialization phase.
+ */
+ if (hwait_sig)
+ {
+ if (GetCurrentThreadId () != sigtid)
+ WaitForSingleObject (h, 10000);
+ ForceCloseHandle1 (h, hwait_sig);
+
+ /* Exiting thread. Cleanup. Don't set to inactive if a child has been
+ execed with the same pid. */
+ if (!myself->dwProcessId || myself->dwProcessId == GetCurrentProcessId ())
+ myself->process_state &= ~PID_ACTIVE;
+ else
+ sip_printf ("Did not clear PID_ACTIVE since %d != %d",
+ myself->dwProcessId, GetCurrentProcessId ());
+
+ /* In case of a sigsuspend */
+ SetEvent (signal_arrived);
+
+ if (GetCurrentThreadId () != sigtid)
+ {
+ ForceCloseHandle (sigcomplete_main);
+ ForceCloseHandle (sigcomplete_nonmain);
+ ForceCloseHandle (sigcatch_main);
+ ForceCloseHandle (sigcatch_nonmain);
+ ForceCloseHandle (sigcatch_nosync);
+ }
+ }
+ sip_printf ("done");
+ }
+
+ /* Set this so that subsequent tests will succeed. */
+ if (!myself->dwProcessId)
+ myself->dwProcessId = GetCurrentProcessId ();
+
+ return;
+}
+
+/* Send a signal to another process by raising its signal semaphore.
+ * If pinfo *p == NULL, send to the current process.
+ * If sending to this process, wait for notification that a signal has
+ * completed before returning.
+ */
+int __stdcall
+sig_send (pinfo *p, int sig)
+{
+ int rc = 1;
+ DWORD tid = GetCurrentThreadId ();
+ BOOL its_me;
+ HANDLE thiscatch = NULL;
+ HANDLE thiscomplete = NULL;
+ BOOL wait_for_completion;
+
+ if (p == myself_nowait_nonmain)
+ p = (tid == maintid) ? myself : myself_nowait;
+ if (!(its_me = (p == NULL || p == myself || p == myself_nowait)))
+ wait_for_completion = FALSE;
+ else
+ {
+ if (no_signals_available ())
+ goto out; // Either exiting or not yet initializing
+ wait_for_me ();
+ wait_for_completion = p != myself_nowait;
+ p = myself;
+ }
+
+ /* It is possible that the process is not yet ready to receive messages
+ * or that it has exited. Detect this.
+ */
+ if (!proc_can_be_signalled (p)) /* Is the process accepting messages? */
+ {
+ sip_printf ("invalid pid %d(%x), signal %d",
+ p->pid, p->process_state, sig);
+ set_errno (ESRCH);
+ goto out;
+ }
+
+ sip_printf ("pid %d, signal %d, its_me %d", p->pid, sig, its_me);
+
+ if (its_me)
+ {
+ if (!wait_for_completion)
+ thiscatch = sigcatch_nosync;
+ else if (tid != maintid)
+ {
+ thiscatch = sigcatch_nonmain;
+ thiscomplete = sigcomplete_nonmain;
+ }
+ else
+ {
+ thiscatch = sigcatch_main;
+ thiscomplete = sigcomplete_main;
+ ResetEvent (thiscomplete);
+ }
+ }
+ else if (!(thiscatch = getsem (p, "sigcatch", 0, 0)))
+ goto out; // Couldn't get the semaphore. getsem issued
+ // an error, if appropriate.
+
+#if WHEN_MULTI_THREAD_SIGNALS_WORK
+ signal_dispatch *sd;
+ sd = signal_dispatch_storage.get ();
+ if (sd == NULL)
+ sd = signal_dispatch_storage.create ();
+#endif
+ /* Increment the sigtodo array to signify which signal to assert.
+ */
+ (void) InterlockedIncrement (p->getsigtodo(sig));
+
+ /* Notify the process that a signal has arrived.
+ */
+ SetLastError (0);
+ if (!ReleaseSemaphore (thiscatch, 1, NULL) && (int) GetLastError () > 0)
+ {
+ /* Couldn't signal the semaphore. This probably means that the
+ * process is exiting.
+ */
+ if (!its_me)
+ ForceCloseHandle (thiscatch);
+ else
+ {
+ if (no_signals_available ())
+ sip_printf ("I'm going away now");
+ else if ((int) GetLastError () == -1)
+ rc = WaitForSingleObject (thiscomplete, 500);
+ else
+ system_printf ("error sending signal %d to pid %d, semaphore %p, %E",
+ sig, p->pid, thiscatch);
+ }
+ goto out;
+ }
+
+ /* No need to wait for signal completion unless this was a signal to
+ * this process.
+ *
+ * If it was a signal to this process, wait for a dispatched signal.
+ * Otherwise just wait for the wait_sig to signal that it has finished
+ * processing the signal.
+ */
+ if (!wait_for_completion)
+ {
+ rc = WAIT_OBJECT_0;
+ sip_printf ("Not waiting for sigcomplete. its_me %d sig %d", its_me, sig);
+ if (!its_me)
+ ForceCloseHandle (thiscatch);
+ }
+ else
+ {
+ sip_printf ("Waiting for thiscomplete %p", thiscomplete);
+
+ SetLastError (0);
+ rc = WaitForSingleObject (thiscomplete, WSSC);
+ /* Check for strangeness due to this thread being redirected by the
+ signal handler. Sometimes a WAIT_TIMEOUT will occur when the
+ thread hasn't really timed out. So, check again.
+ FIXME: This isn't foolproof. */
+ if (rc != WAIT_OBJECT_0 &&
+ WaitForSingleObject (thiscomplete, 0) == WAIT_OBJECT_0)
+ rc = WAIT_OBJECT_0;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ rc = 0; // Successful exit
+ else
+ {
+ /* It's an error unless sig_loop_wait == 0 (the process is exiting). */
+ if (!no_signals_available ())
+ system_printf ("wait for sig_complete event failed, sig %d, rc %d, %E",
+ sig, rc);
+ set_errno (ENOSYS);
+ rc = -1;
+ }
+
+out:
+ sip_printf ("returning %d from sending signal %d", rc, sig);
+ return rc;
+}
+
+/* Set pending signal from the sigtodo array
+ */
+void __stdcall
+sig_set_pending (int sig)
+{
+ (void) InterlockedIncrement (myself->getsigtodo(sig));
+ return;
+}
+
+/* Initialize the wait_subproc thread.
+ * Called from fork() or spawn() to initialize the handling of subprocesses.
+ */
+void __stdcall
+subproc_init (void)
+{
+ if (hwait_subproc)
+ return;
+
+ /* A "wakeup" handle which can be toggled to make wait_subproc reexamine
+ * the hchildren array.
+ */
+ events[0] = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ if (!(hwait_subproc = makethread (wait_subproc, NULL, 0, "+proc")))
+ system_printf ("cannot create wait_subproc thread, %E");
+ ProtectHandle (events[0]);
+ ProtectHandle (hwait_subproc);
+ sip_printf ("started wait_subproc thread %p", hwait_subproc);
+}
+
+/* Initialize some of the memory block passed to child processes
+ by fork/spawn/exec. */
+
+void __stdcall
+init_child_info (DWORD chtype, child_info *ch, int pid, HANDLE subproc_ready)
+{
+ subproc_init ();
+ memset (ch, 0, sizeof *ch);
+ ch->cb = sizeof *ch;
+ ch->type = chtype;
+ ch->cygpid = pid;
+ ch->shared_h = cygwin_shared_h;
+ ch->console_h = console_shared_h;
+ ch->subproc_ready = subproc_ready;
+ if (chtype != PROC_EXEC || !parent_alive)
+ ch->parent_alive = hwait_subproc;
+ else if (parent_alive)
+ DuplicateHandle (hMainProc, parent_alive, hMainProc, &ch->parent_alive,
+ 0, 1, DUPLICATE_SAME_ACCESS);
+}
+
+/* Check the state of all of our children to see if any are stopped or
+ * terminated.
+ */
+static int __stdcall
+checkstate (waitq *w)
+{
+ int i, x, potential_match = 0;
+ pinfo *child;
+
+ sip_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+
+ /* Check already dead processes first to see if they match the criteria
+ * given in w->next.
+ */
+ for (i = 0; i < nzombies; i++)
+ if ((x = stopped_or_terminated (w, child = zombies[i])) < 0)
+ potential_match = -1;
+ else if (x > 0)
+ {
+ remove_zombie (i);
+ potential_match = 1;
+ goto out;
+ }
+
+ sip_printf ("checking alive children");
+
+ /* No dead terminated children matched. Check for stopped children. */
+ for (i = 0; i < nchildren; i++)
+ if ((x = stopped_or_terminated (w, pchildren[i])) < 0)
+ potential_match = -1;
+ else if (x > 0)
+ {
+ potential_match = 1;
+ break;
+ }
+
+out:
+ sip_printf ("returning %d", potential_match);
+ return potential_match;
+}
+
+/* Get or create a process specific semaphore used in message passing.
+ */
+static HANDLE __stdcall
+getsem (pinfo *p, const char *str, int init, int max)
+{
+ HANDLE h;
+
+ if (p != NULL)
+ {
+ if (!proc_can_be_signalled (p))
+ {
+ set_errno (ESRCH);
+ return NULL;
+ }
+ int wait = 10000;
+ sip_printf ("pid %d, ppid %d, wait %d, initializing %x", p->pid, p->ppid, wait,
+ ISSTATE (p, PID_INITIALIZING));
+ for (int i = 0; ISSTATE (p, PID_INITIALIZING) && i < wait; i++)
+ Sleep (1);
+ }
+
+ SetLastError (0);
+ if (p == NULL)
+ {
+ char sa_buf[1024];
+
+ DWORD winpid = GetCurrentProcessId ();
+ h = CreateSemaphore (allow_ntsec ? sec_user (sa_buf) : &sec_none_nih,
+ init, max, str = shared_name (str, winpid));
+ p = myself;
+ }
+ else
+ {
+ h = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+ str = shared_name (str, p->dwProcessId));
+
+ if (h == NULL)
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND && !proc_exists (p))
+ set_errno (ESRCH);
+ else
+ set_errno (EPERM);
+ return NULL;
+ }
+ }
+
+ if (!h)
+ {
+ system_printf ("can't %s %s, %E", p ? "open" : "create", str);
+ set_errno (ESRCH);
+ }
+ return h;
+}
+
+/* Get the sync_proc_subproc muto to control access to
+ * children, zombie arrays.
+ * Attempt to handle case where process is exiting as we try to grab
+ * the mutex.
+ */
+static BOOL __inline
+get_proc_lock (DWORD what, DWORD val)
+{
+ Static int lastwhat = -1;
+ if (!sync_proc_subproc)
+ return FALSE;
+ if (sync_proc_subproc->acquire (WPSP))
+ {
+ lastwhat = what;
+ return TRUE;
+ }
+ if (!sync_proc_subproc)
+ return FALSE;
+ system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), %E, last %d",
+ what, val, lastwhat);
+ return TRUE;
+}
+
+/* Remove a child from pchildren/hchildren by swapping it with the
+ * last child in the list.
+ */
+static void __stdcall
+remove_child (int ci)
+{
+ sip_printf ("removing [%d], pid %d, handle %p, nchildren %d",
+ ci, pchildren[ci]->pid, hchildren[ci], nchildren);
+ if (ci < --nchildren)
+ {
+ pchildren[ci] = pchildren[nchildren];
+ hchildren[ci] = hchildren[nchildren];
+ }
+
+ return;
+}
+
+/* Remove a zombie from zombies by swapping it with the last child in the list.
+ */
+static void __stdcall
+remove_zombie (int ci)
+{
+ sip_printf ("removing %d, pid %d, nzombies %d", ci, zombies[ci]->pid,
+ nzombies);
+ if (ci < --nzombies)
+ zombies[ci] = zombies[nzombies];
+
+ return;
+}
+
+/* Check status of child process vs. waitq member.
+ *
+ * parent_w is the pointer to the parent of the waitq member in question.
+ * child is the subprocess being considered.
+ *
+ * Returns
+ * 1 if stopped or terminated child matches parent_w->next criteria
+ * -1 if a non-stopped/terminated child matches parent_w->next criteria
+ * 0 if child does not match parent_w->next criteria
+ */
+static int __stdcall
+stopped_or_terminated (waitq *parent_w, pinfo *child)
+{
+ int potential_match;
+ waitq *w = parent_w->next;
+
+ sip_printf ("considering pid %d", child->pid);
+ if (w->pid == -1)
+ potential_match = 1;
+ else if (w->pid == 0)
+ potential_match = child->pgid == myself->pgid;
+ else if (w->pid < 0)
+ potential_match = child->pgid == -w->pid;
+ else
+ potential_match = (w->pid == child->pid);
+
+ if (!potential_match)
+ return 0;
+
+ BOOL terminated;
+
+ if ((terminated = child->process_state == PID_ZOMBIE) ||
+ (w->options & WUNTRACED) && child->stopsig)
+ {
+ parent_w->next = w->next; /* successful wait. remove from wait queue */
+ w->pid = child->pid;
+
+ if (!terminated)
+ {
+ sip_printf ("stopped child");
+ w->status = (child->stopsig << 8) | 0x7f;
+ child->stopsig = 0;
+ }
+ else
+ {
+ DWORD status;
+ if (!GetExitCodeProcess (child->hProcess, &status))
+ status = 0xffff;
+ if (status & EXIT_SIGNAL)
+ w->status = (status >> 8) & 0xff; /* exited due to signal */
+ else
+ w->status = (status & 0xff) << 8; /* exited via "exit ()" */
+
+ add_rusage (&myself->rusage_children, &child->rusage_children);
+ add_rusage (&myself->rusage_children, &child->rusage_self);
+
+ if (w->rusage)
+ {
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_children);
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_self);
+ }
+ ForceCloseHandle1 (child->hProcess, childhProc);
+ child->hProcess = NULL;
+ child->process_state = PID_NOT_IN_USE; /* a reaped child */
+ }
+
+ if (!SetEvent (w->ev)) /* wake up wait4 () immediately */
+ system_printf ("couldn't wake up wait event %p, %E", w->ev);
+ return 1;
+ }
+
+ return -potential_match;
+}
+
+/* Process signals by waiting for a semaphore to become signaled.
+ * Then scan an in-memory array representing queued signals.
+ * Executes in a separate thread.
+ *
+ * Signals sent from this process are sent a completion signal so
+ * that returns from kill/raise do not occur until the signal has
+ * has been handled, as per POSIX.
+ */
+static DWORD WINAPI
+wait_sig (VOID *arg)
+{
+ /* Initialization */
+ (void) SetThreadPriority (hwait_sig, WAIT_SIG_PRIORITY);
+
+ /* sigcatch_nosync - semaphore incremented by sig_dispatch_pending and
+ * by foreign processes to force an examination of
+ * the sigtodo array.
+ * sigcatch_main - ditto for local main thread.
+ * sigcatch_nonmain - ditto for local non-main threads.
+ *
+ * sigcomplete_main - event used to signal main thread on signal
+ * completion
+ * sigcomplete_nonmain - semaphore signaled for non-main thread on signal
+ * completion
+ */
+ sigcatch_nosync = getsem (NULL, "sigcatch", 0, MAXLONG);
+ sigcatch_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL);
+ sigcatch_main = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL);
+ sigcomplete_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL);
+ sigcomplete_main = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ sigproc_printf ("sigcatch_nonmain %p", sigcatch_nonmain);
+
+ /* Setting dwProcessId flags that this process is now capable of receiving
+ * signals. Prior to this, dwProcessId was set to the windows pid of
+ * of the original windows process which spawned us unless this was a
+ * "toplevel" process.
+ */
+ myself->dwProcessId = GetCurrentProcessId ();
+ myself->process_state |= PID_ACTIVE;
+ myself->process_state &= ~PID_INITIALIZING;
+
+ ProtectHandle (sigcatch_nosync);
+ ProtectHandle (sigcatch_nonmain);
+ ProtectHandle (sigcatch_main);
+ ProtectHandle (sigcomplete_nonmain);
+ ProtectHandle (sigcomplete_main);
+
+ /* If we've been execed, then there is still a stub left in the previous
+ * windows process waiting to see if it's started a cygwin process or not.
+ * Signalling subproc_ready indicates that we are a cygwin process.
+ */
+ if (child_proc_info && child_proc_info->type == PROC_EXEC)
+ {
+ debug_printf ("subproc_ready %p", child_proc_info->subproc_ready);
+ if (!SetEvent (child_proc_info->subproc_ready))
+ system_printf ("SetEvent (subproc_ready) failed, %E");
+ ForceCloseHandle (child_proc_info->subproc_ready);
+ }
+
+ SetEvent (wait_sig_inited);
+ sigtid = GetCurrentThreadId ();
+
+ /* If we got something like a SIGINT while we were initializing, the
+ signal thread should be waiting for this event. This signals the
+ thread that it's ok to send the signal since the wait_sig thread
+ is now active. */
+ extern HANDLE console_handler_thread_waiter;
+ SetEvent (console_handler_thread_waiter);
+
+ HANDLE catchem[] = {sigcatch_main, sigcatch_nonmain, sigcatch_nosync};
+ sigproc_printf ("Ready. dwProcessid %d", myself->dwProcessId);
+ for (;;)
+ {
+ DWORD rc = WaitForMultipleObjects (3, catchem, FALSE, sig_loop_wait);
+
+ /* sigproc_terminate sets sig_loop_wait to zero to indicate that
+ * this thread should terminate.
+ */
+ if (rc == WAIT_TIMEOUT)
+ if (!sig_loop_wait)
+ break; // Exiting
+ else
+ continue;
+
+ if (rc == WAIT_FAILED)
+ {
+ if (sig_loop_wait != 0)
+ system_printf ("WFMO failed, %E");
+ break;
+ }
+
+ rc -= WAIT_OBJECT_0;
+ int dispatched = FALSE;
+ sip_printf ("awake");
+ /* A sigcatch semaphore has been signaled. Scan the sigtodo
+ * array looking for any unprocessed signals.
+ */
+ pending_signals = 0;
+ for (int sig = -__SIGOFFSET; sig < NSIG; sig++)
+ {
+#ifdef NOSIGQUEUE
+ if (InterlockedExchange (myself->getsigtodo(sig), 0L) > 0)
+#else
+ while (InterlockedDecrement (myself->getsigtodo(sig)) >= 0)
+#endif
+ {
+ if (sig > 0 && sig != SIGCONT && sig != SIGKILL && sig != SIGSTOP &&
+ (sigismember (& myself->getsigmask (), sig) ||
+ myself->process_state & PID_STOPPED))
+ {
+ sip_printf ("sig %d blocked", sig);
+ break;
+ }
+
+ /* Found a signal to process */
+ sip_printf ("processing signal %d", sig);
+ switch (sig)
+ {
+ case __SIGFLUSH:
+ /* just forcing the loop */
+ break;
+
+ /* Internal signal to force a flush of strace data to disk. */
+ case __SIGSTRACE:
+ // proc_strace (); // Dump cached strace_printf stuff.
+ break;
+
+ /* Signalled from a child process that it has stopped */
+ case __SIGCHILDSTOPPED:
+ sip_printf ("Received child stopped notification");
+ dispatched |= sig_handle (SIGCHLD);
+ if (proc_subproc (PROC_CHILDSTOPPED, 0))
+ dispatched |= 1;
+ break;
+
+ /* A normal UNIX signal */
+ default:
+ sip_printf ("Got signal %d", sig);
+ dispatched |= sig_handle (sig);
+ goto nextsig;
+ }
+ }
+#ifndef NOSIGQUEUE
+ /* Decremented too far. */
+ if (InterlockedIncrement (myself->getsigtodo(sig)) > 0)
+ pending_signals = 1;
+#endif
+ nextsig:
+ continue;
+ }
+
+ /* Signal completion of signal handling depending on which semaphore
+ * woke up the WaitForMultipleObjects above.
+ */
+ switch (rc)
+ {
+ case 0:
+ SetEvent (sigcomplete_main);
+ break;
+ case 1:
+ ReleaseSemaphore (sigcomplete_nonmain, 1, NULL);
+ break;
+ default:
+ /* Signal from another process. No need to synchronize. */
+ break;
+ }
+
+ if (dispatched < 0)
+ pending_signals = 1;
+ sip_printf ("looping");
+ }
+
+ sip_printf ("done");
+ return 0;
+}
+
+/* Wait for subprocesses to terminate. Executes in a separate thread. */
+static DWORD WINAPI
+wait_subproc (VOID *arg)
+{
+ sip_printf ("starting");
+ int errloop = 0;
+
+ for (;;)
+ {
+ DWORD rc = WaitForMultipleObjects (nchildren + 1, events, FALSE,
+ proc_loop_wait);
+ if (rc == WAIT_TIMEOUT)
+ if (!proc_loop_wait)
+ break; // Exiting
+ else
+ continue;
+
+ if (rc == WAIT_FAILED)
+ {
+ if (!proc_loop_wait)
+ break;
+
+ /* It's ok to get an ERROR_INVALID_HANDLE since another thread may have
+ closed a handle in the children[] array. So, we try looping a couple
+ of times to stabilize. FIXME - this is not foolproof. Probably, this
+ thread should be responsible for closing the children. */
+ if (++errloop < 10 && GetLastError () == ERROR_INVALID_HANDLE)
+ continue;
+
+ system_printf ("wait failed. nchildren %d, wait %d, %E",
+ nchildren, proc_loop_wait);
+
+ for (int i = 0; i < nchildren + 1; i++)
+ if ((rc = WaitForSingleObject (events[i], 0)) == WAIT_OBJECT_0 ||
+ rc == WAIT_TIMEOUT)
+ continue;
+ else
+ system_printf ("event[%d] %p, %E", i, events[0]);
+ break;
+ }
+
+ errloop = 0;
+ rc -= WAIT_OBJECT_0;
+ if (rc-- != 0)
+ (void)proc_subproc (PROC_CHILDTERMINATED, rc);
+ sip_printf ("looping");
+ }
+
+ ForceCloseHandle (events[0]);
+ events[0] = NULL;
+ sip_printf ("done");
+ return 0;
+}