summaryrefslogtreecommitdiff
path: root/winsup/cygwin/sigproc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/sigproc.cc')
-rw-r--r--winsup/cygwin/sigproc.cc1379
1 files changed, 1379 insertions, 0 deletions
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
new file mode 100644
index 00000000000..ed927ed3663
--- /dev/null
+++ b/winsup/cygwin/sigproc.cc
@@ -0,0 +1,1379 @@
+/* sigproc.cc: inter/intra signal and sub process handler
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ 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 "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <sys/signal.h>
+#include "cygerrno.h"
+#include "sync.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "shared_info.h"
+#include "cygthread.h"
+
+/*
+ * 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 TOTSIGS (NSIG + __SIGOFFSET)
+
+#define wake_wait_subproc() SetEvent (events[0])
+
+#define no_signals_available() (!hwait_sig || !sig_loop_wait)
+
+#define NZOMBIES 256
+
+static LONG local_sigtodo[TOTSIGS];
+struct sigaction *global_sigs;
+
+inline LONG *
+getlocal_sigtodo (int sig)
+{
+ return local_sigtodo + __SIGOFFSET + sig;
+}
+
+void __stdcall
+sigalloc ()
+{
+ cygheap->sigs = global_sigs =
+ (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction));
+}
+
+void __stdcall
+signal_fixup_after_exec ()
+{
+ global_sigs = cygheap->sigs;
+ /* Set up child's signal handlers */
+ for (int i = 0; i < NSIG; i++)
+ {
+ myself->getsig (i).sa_mask = 0;
+ if (myself->getsig (i).sa_handler != SIG_IGN)
+ myself->getsig (i).sa_handler = SIG_DFL;
+ }
+}
+
+/*
+ * 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 = 1000; // Wait for subprocesses to exit
+Static DWORD sig_loop_wait = INFINITE; // Wait for signals to arrive
+
+Static HANDLE sigcatch_nonmain; // The semaphore signaled when
+ // signals are available for
+ // processing from non-main thread
+Static HANDLE sigcatch_main; // Signalled when main thread sends a
+ // signal
+Static HANDLE sigcatch_nosync; // Signal wait_sig to scan sigtodo
+ // but not to bother with any
+ // synchronization
+Static HANDLE sigcomplete_main; // Event signaled when a signal has
+ // finished processing for the main
+ // thread
+Static HANDLE sigcomplete_nonmain; // Semaphore raised for non-main
+ // threads when a signal has finished
+ // processing
+HANDLE NO_COPY sigCONT; // Used to "STOP" a process
+Static cygthread *hwait_sig; // Handle of wait_sig thread
+Static cygthread *hwait_subproc; // Handle of sig_subproc thread
+
+Static HANDLE wait_sig_inited; // Control synchronization of
+ // message queue startup
+
+/* Used by WaitForMultipleObjects. These are handles to child processes.
+ */
+Static HANDLE events[PSIZE + 1]; // All my children's handles++
+#define hchildren (events + 1) // Where the children handles begin
+Static char cpchildren[PSIZE * sizeof (pinfo)]; // All my children info
+Static int nchildren; // Number of active children
+Static char czombies[(NZOMBIES + 1) * sizeof (pinfo)]; // All my deceased children info
+Static int nzombies; // Number of deceased children
+
+#define pchildren ((pinfo *) cpchildren)
+#define zombies ((pinfo *) czombies)
+
+Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads
+Static waitq waitq_main; // Storage for main thread
+
+muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff
+
+DWORD NO_COPY sigtid = 0; // ID of the signal thread
+
+bool NO_COPY pending_signals = false; // true if signals pending
+
+/* Functions
+ */
+static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1)));
+static __inline__ BOOL get_proc_lock (DWORD, DWORD);
+static HANDLE __stdcall getevent (_pinfo *, const char *) __attribute__ ((regparm (2)));
+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 (!myself->ppid_handle)
+ {
+ debug_printf ("No myself->ppid_handle");
+ res = FALSE;
+ }
+ else
+ for (int i = 0; i < 2; i++)
+ switch (res = WaitForSingleObject (myself->ppid_handle, 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 myself->ppid_handle(%p) failed, error %d",
+ myself->ppid_handle, werr);
+ res = FALSE;
+ goto out;
+ }
+out:
+ return res;
+}
+
+void __stdcall
+wait_for_sigthread ()
+{
+ sigproc_printf ("wait_sig_inited %p", wait_sig_inited);
+ HANDLE hsig_inited = wait_sig_inited;
+ (void) WaitForSingleObject (hsig_inited, INFINITE);
+ wait_sig_inited = NULL;
+ (void) ForceCloseHandle1 (hsig_inited, wait_sig_inited);
+}
+
+/* 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
+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;
+}
+
+static BOOL __stdcall
+proc_can_be_signalled (_pinfo *p)
+{
+ if (p == myself_nowait || p == myself_nowait_nonmain || p == myself)
+ {
+ assert (!wait_sig_inited);
+ return 1;
+ }
+
+ return ISSTATE (p, PID_INITIALIZING) ||
+ (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) ==
+ (PID_ACTIVE | PID_IN_USE));
+}
+
+BOOL __stdcall
+pid_exists (pid_t pid)
+{
+ pinfo p (pid);
+ return proc_exists (p);
+}
+
+/* Test to determine if a process really exists and is processing signals.
+ */
+BOOL __stdcall
+proc_exists (_pinfo *p)
+{
+ return p && !(p->process_state & PID_EXITED);
+}
+
+/* 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. */
+int __stdcall
+mychild (int pid)
+{
+ for (int i = 0; i < nchildren; i++)
+ if (pchildren[i]->pid == pid)
+ return 1;
+ for (int i = 0; i < nzombies; i++)
+ if (zombies[i]->pid == pid)
+ return 1;
+ return 0;
+}
+
+/* Handle all subprocess requests
+ */
+#define vchild (*((pinfo *) val))
+int __stdcall
+proc_subproc (DWORD what, DWORD val)
+{
+ int rc = 1;
+ int potential_match;
+ _pinfo *child;
+ int clearing;
+ waitq *w;
+
+#define wval ((waitq *) val)
+
+ sigproc_printf ("args: %x, %d", what, val);
+
+ if (!get_proc_lock (what, val)) // Serialize access to this function
+ {
+ system_printf ("couldn't get proc lock. Something is wrong.");
+ 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)
+ {
+ rc = 0;
+ break;
+ }
+ pchildren[nchildren] = vchild;
+ hchildren[nchildren] = vchild->hProcess;
+ if (!DuplicateHandle (hMainProc, vchild->hProcess, hMainProc, &vchild->pid_handle,
+ 0, 0, DUPLICATE_SAME_ACCESS))
+ system_printf ("Couldn't duplicate child handle for pid %d, %E", vchild->pid);
+ ProtectHandle1 (vchild->pid_handle, pid_handle);
+
+ if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid);
+ 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->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY);
+
+ sigproc_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 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:
+ if (hchildren[val] != pchildren[val]->hProcess)
+ {
+ sigproc_printf ("pid %d[%d], reparented old hProcess %p, new %p",
+ pchildren[val]->pid, val, hchildren[val], pchildren[val]->hProcess);
+ HANDLE h = hchildren[val];
+ hchildren[val] = pchildren[val]->hProcess; /* Filled out by child */
+ sync_proc_subproc->release (); // Release the lock ASAP
+ ForceCloseHandle1 (h, childhProc);
+ ProtectHandle1 (pchildren[val]->hProcess, childhProc);
+ rc = 0;
+ goto out; // This was an exec()
+ }
+
+ sigproc_printf ("pid %d[%d] terminated, handle %p, nchildren %d, nzombies %d",
+ pchildren[val]->pid, val, hchildren[val], nchildren, nzombies);
+
+ int thiszombie;
+ thiszombie = nzombies;
+ zombies[nzombies] = pchildren[val]; // Add to zombie array
+ zombies[nzombies++]->process_state = PID_ZOMBIE;// Walking dead
+
+ sigproc_printf ("zombifying [%d], pid %d, handle %p, nchildren %d",
+ val, pchildren[val]->pid, hchildren[val], nchildren);
+ if ((int) val < --nchildren)
+ {
+ hchildren[val] = hchildren[nchildren];
+ pchildren[val] = pchildren[nchildren];
+ }
+
+ /* See if we should care about the this terminated process. If we've
+ filled up our table or if we're ignoring SIGCHLD, then we immediately
+ remove the process and move on. Otherwise, this process becomes a zombie
+ which must be reaped by a wait() call. */
+ if (nzombies >= NZOMBIES
+ || myself->getsig (SIGCHLD).sa_handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("automatically removing zombie %d", thiszombie);
+ remove_zombie (thiszombie);
+ }
+
+ /* Don't scan the wait queue yet. Caller will send SIGCHLD to this process.
+ This will cause an eventual scan of waiters. */
+ 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 (!mychild (wval->pid))
+ goto out; // invalid pid. flag no such child
+
+ wval->status = 0; // Don't know status yet
+ sigproc_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);
+ w = waitq_head.next;
+ waitq_head.next = wval; /* Add at the beginning. */
+ wval->next = w; /* Link in rest of the list. */
+ clearing = 0;
+ goto scan_wait;
+
+ /* 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. */
+ if (val)
+ sigproc_printf ("clear waiting threads");
+ else
+ sigproc_printf ("looking for processes to reap");
+ clearing = val;
+
+ 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)
+ sigproc_printf ("released waiting thread");
+ else if (!clearing && !(w->next->options & WNOHANG) && potential_match < 0)
+ sigproc_printf ("only found non-terminated children");
+ else if (potential_match <= 0) // nothing matched
+ {
+ sigproc_printf ("waiting thread found no children");
+ HANDLE oldw = w->next->ev;
+ w->next->pid = 0;
+ if (clearing)
+ w->next->status = -1; /* flag that a signal was received */
+ else if (!potential_match || !(w->next->options & WNOHANG))
+ 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;
+ }
+
+ if (!clearing)
+ sigproc_printf ("finished processing terminated/stopped child");
+ else
+ {
+ waitq_head.next = NULL;
+ sigproc_printf ("finished clearing");
+ }
+ break;
+ }
+
+out:
+ sync_proc_subproc->release (); // Release the lock
+out1:
+ sigproc_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)
+{
+ sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+ /* Signal processing is assumed to be blocked in this routine. */
+ if (hwait_subproc)
+ {
+ proc_loop_wait = 0; // Tell wait_subproc thread to exit
+ sync_proc_subproc->acquire (WPSP);
+ wake_wait_subproc (); // Wake wait_subproc loop
+ hwait_subproc = NULL;
+
+ (void) proc_subproc (PROC_CLEARWAIT, 1);
+
+ /* Clean out zombie processes from the pid list. */
+ int i;
+ for (i = 0; i < nzombies; i++)
+ {
+ if (zombies[i]->hProcess)
+ {
+ ForceCloseHandle1 (zombies[i]->hProcess, childhProc);
+ ForceCloseHandle1 (zombies[i]->pid_handle, pid_handle);
+ }
+ zombies[i]->ppid = 1;
+ zombies[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */
+ zombies[i].release (); // FIXME: this breaks older gccs for some reason
+ }
+
+ /* Disassociate my subprocesses */
+ for (i = 0; i < nchildren; i++)
+ {
+ if (!pchildren[i]->hProcess)
+ sigproc_printf ("%d(%d) hProcess cleared already?", pchildren[i]->pid,
+ pchildren[i]->dwProcessId);
+ else
+ {
+ ForceCloseHandle1 (pchildren[i]->hProcess, childhProc);
+ sigproc_printf ("%d(%d) closed child handle", pchildren[i]->pid,
+ pchildren[i]->dwProcessId);
+ pchildren[i]->ppid = 1;
+ if (pchildren[i]->pgid == myself->pid)
+ pchildren[i]->process_state |= PID_ORPHANED;
+ }
+ pchildren[i].release ();
+ }
+ nchildren = nzombies = 0;
+ /* Just zero sync_proc_subproc as the delete below seems to cause
+ problems for older gccs. */
+ sync_proc_subproc = NULL;
+ }
+ sigproc_printf ("leaving");
+}
+
+/* Clear pending signal from the sigtodo array
+ */
+void __stdcall
+sig_clear (int sig)
+{
+ (void) InterlockedExchange (myself->getsigtodo (sig), 0L);
+ (void) InterlockedExchange (getlocal_sigtodo (sig), 0L);
+ return;
+}
+
+extern "C" int
+sigpending (sigset_t *set)
+{
+ unsigned bit;
+ *set = 0;
+ for (int sig = 1; sig < NSIG; sig++)
+ if ((*getlocal_sigtodo (sig) || *myself->getsigtodo (sig))
+ && (myself->getsigmask () & (bit = SIGTOMASK (sig))))
+ *set |= bit;
+ return 0;
+}
+
+/* Force the wait_sig thread to wake up and scan the sigtodo array.
+ */
+extern "C" int __stdcall
+sig_dispatch_pending ()
+{
+ if (exit_state || !hwait_sig || GetCurrentThreadId () == sigtid)
+ return 0;
+
+ sigframe thisframe (mainthread);
+
+#ifdef DEBUGGING
+ sigproc_printf ("pending_signals %d", pending_signals);
+#endif
+
+ if (!pending_signals)
+#ifdef DEBUGGING
+ sigproc_printf ("no need to wake anything up");
+#else
+ ;
+#endif
+ else
+ {
+ (void) sig_send (myself, __SIGFLUSH);
+#ifdef DEBUGGING
+ sigproc_printf ("woke up wait_sig");
+#endif
+ }
+
+ return thisframe.call_signal_handler ();
+}
+
+/* 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, TRUE, FALSE, NULL);
+ ProtectHandle (wait_sig_inited);
+
+ /* sync_proc_subproc is used by proc_subproc. It serialises
+ * access to the children and zombie arrays.
+ */
+ new_muto (sync_proc_subproc);
+
+ /* local event signaled when main thread has been dispatched
+ to a signal handler function. */
+ signal_arrived = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (signal_arrived);
+
+ hwait_sig = new cygthread (wait_sig, cygself, "sig");
+ hwait_sig->zap_h ();
+
+ /* 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
+
+ myself->getsig (SIGSTOP).sa_flags = SA_RESTART | SA_NODEFER;
+ sigproc_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)
+{
+ hwait_sig = NULL;
+
+ if (!sig_loop_wait)
+ sigproc_printf ("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
+ 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
+
+ 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, DWORD ebp, bool exception)
+{
+ int rc = 1;
+ DWORD tid = GetCurrentThreadId ();
+ BOOL its_me;
+ HANDLE thiscatch = NULL;
+ HANDLE thiscomplete = NULL;
+ BOOL wait_for_completion;
+ sigframe thisframe;
+
+ if (p == myself_nowait_nonmain)
+ p = (tid == mainthread.id) ? (_pinfo *) 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
+ if (wait_sig_inited)
+ wait_for_sigthread ();
+ 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? */
+ {
+ sigproc_printf ("invalid pid %d(%x), signal %d",
+ p->pid, p->process_state, sig);
+ set_errno (ESRCH);
+ goto out;
+ }
+
+ sigproc_printf ("pid %d, signal %d, its_me %d", p->pid, sig, its_me);
+
+ LONG *todo;
+ bool issem;
+ if (its_me)
+ {
+ if (!wait_for_completion)
+ {
+ thiscatch = sigcatch_nosync;
+ todo = myself->getsigtodo (sig);
+ issem = false;
+ }
+ else if (tid != mainthread.id)
+ {
+ thiscatch = sigcatch_nonmain;
+ thiscomplete = sigcomplete_nonmain;
+ todo = getlocal_sigtodo (sig);
+ issem = true;
+ }
+ else
+ {
+ thiscatch = sigcatch_main;
+ thiscomplete = sigcomplete_main;
+ thisframe.init (mainthread, ebp, exception);
+ todo = getlocal_sigtodo (sig);
+ issem = true;
+ }
+ }
+ else if ((thiscatch = getevent (p, "sigcatch")))
+ {
+ todo = p->getsigtodo (sig);
+ issem = false;
+ }
+ else
+ goto out; // Couldn't get the semaphore. getevent 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 (todo);
+
+ /* Notify the process that a signal has arrived.
+ */
+ if (issem ? !ReleaseSemaphore (thiscatch, 1, NULL) : !SetEvent (thiscatch))
+ {
+ /* Couldn't signal the semaphore. This probably means that the
+ * process is exiting.
+ */
+ if (!its_me)
+ ForceCloseHandle (thiscatch);
+ else
+ {
+ if (no_signals_available ())
+ sigproc_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;
+ sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d", its_me, sig);
+ if (!its_me)
+ ForceCloseHandle (thiscatch);
+ }
+ else
+ {
+ sigproc_printf ("Waiting for thiscomplete %p", thiscomplete);
+ rc = WaitForSingleObject (thiscomplete, WSSC);
+ }
+
+ 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, signal %d, rc %d, %E",
+ sig, rc);
+ set_errno (ENOSYS);
+ rc = -1;
+ }
+
+out:
+ sigproc_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 (getlocal_sigtodo (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);
+ hwait_subproc = new cygthread (wait_subproc, NULL, "proc");
+ hwait_subproc->zap_h ();
+ ProtectHandle (events[0]);
+ sigproc_printf ("started wait_subproc thread");
+}
+
+/* Initialize some of the memory block passed to child processes
+ by fork/spawn/exec. */
+
+void __stdcall
+init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready)
+{
+ 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->cygpid = pid;
+ ch->subproc_ready = subproc_ready;
+ ch->pppid_handle = myself->ppid_handle;
+ ch->fhandler_union_cb = sizeof (fhandler_union);
+ ch->mount_h = cygwin_mount_h;
+}
+
+/* Check the state of all of our children to see if any are stopped or
+ * terminated.
+ */
+static int __stdcall
+checkstate (waitq *parent_w)
+{
+ int potential_match = 0;
+
+ sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+
+ /* Check already dead processes first to see if they match the criteria
+ * given in w->next.
+ */
+ for (int i = 0; i < nzombies; i++)
+ switch (stopped_or_terminated (parent_w, zombies[i]))
+ {
+ case -1:
+ potential_match = -1;
+ break;
+ case 1:
+ remove_zombie (i);
+ potential_match = 1;
+ goto out;
+ }
+
+ sigproc_printf ("checking alive children");
+
+ /* No dead terminated children matched. Check for stopped children. */
+ for (int i = 0; i < nchildren; i++)
+ switch (stopped_or_terminated (parent_w, pchildren[i]))
+ {
+ case -1:
+ potential_match = -1;
+ break;
+ case 1:
+ potential_match = 1;
+ goto out;
+ }
+
+out:
+ sigproc_printf ("returning %d", potential_match);
+ return potential_match;
+}
+
+/* Get or create a process specific semaphore used in message passing.
+ */
+static HANDLE __stdcall
+getevent (_pinfo *p, const char *str)
+{
+ HANDLE h;
+ char sem_name[MAX_PATH];
+
+ if (p != NULL)
+ {
+ if (!proc_can_be_signalled (p))
+ {
+ set_errno (ESRCH);
+ return NULL;
+ }
+ int wait = 1000;
+ /* Wait for new process to generate its semaphores. */
+ sigproc_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++)
+ low_priority_sleep (1);
+ }
+
+ if (p == NULL)
+ {
+ char sa_buf[1024];
+
+ DWORD winpid = GetCurrentProcessId ();
+#if 0
+ h = CreateSemaphore (sec_user_nih (sa_buf), init, max,
+ str = shared_name (sem_name, str, winpid));
+#else
+ h = CreateEvent (sec_user_nih (sa_buf), FALSE, FALSE,
+ str = shared_name (sem_name, str, winpid));
+#endif
+ p = myself;
+ if (!h)
+ {
+ system_printf ("can't create semaphore %s, %E", str);
+ __seterrno ();
+ }
+ }
+ else
+ {
+#if 0
+ h = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+ shared_name (sem_name, str, p->dwProcessId));
+#else
+ h = OpenEvent (EVENT_ALL_ACCESS, FALSE,
+ shared_name (sem_name, str, p->dwProcessId));
+#endif
+
+ if (!h)
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND && !proc_exists (p))
+ set_errno (ESRCH); /* No such process */
+ else
+ set_errno (EPERM); /* Couldn't access the semaphore --
+ different cygwin DLL maybe? */
+ }
+ }
+
+ return h;
+}
+
+/* Remove a zombie from zombies by swapping it with the last child in the list.
+ */
+static void __stdcall
+remove_zombie (int ci)
+{
+ sigproc_printf ("removing %d, pid %d, nzombies %d", ci, zombies[ci]->pid,
+ nzombies);
+
+ if (zombies[ci])
+ {
+ ForceCloseHandle1 (zombies[ci]->hProcess, childhProc);
+ ForceCloseHandle1 (zombies[ci]->pid_handle, pid_handle);
+ zombies[ci].release ();
+ }
+
+ 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;
+
+ sigproc_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)
+ {
+ sigproc_printf ("stopped child");
+ w->status = (child->stopsig << 8) | 0x7f;
+ child->stopsig = 0;
+ }
+ else /* Should only get here when child has been moved to the zombies array */
+ {
+ 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);
+ }
+ }
+
+ 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;
+}
+
+static void
+talktome ()
+{
+ winpids pids;
+ for (unsigned i = 0; i < pids.npids; i++)
+ if (pids[i]->hello_pid == myself->pid)
+ pids[i]->commune_recv ();
+}
+
+#define RC_MAIN 0
+#define RC_NONMAIN 1
+#define RC_NOSYNC 2
+/* 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 *self)
+{
+ LONG *todos[] = {getlocal_sigtodo (0), myself->getsigtodo (0)};
+ /* Initialization */
+ (void) SetThreadPriority (GetCurrentThread (), 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 = getevent (NULL, "sigcatch");
+ 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, FALSE, FALSE, NULL);
+ sigproc_printf ("sigcatch_nonmain %p, sigcatch_main %p", sigcatch_nonmain, sigcatch_main);
+ sigCONT = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+
+ /* 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");
+ ForceCloseHandle1 (child_proc_info->subproc_ready, subproc_ready);
+ /* Initialize an "indirect" pid block so that if someone looks up this
+ process via its Windows PID it will be redirected to the appropriate
+ Cygwin PID shared memory block. */
+ static pinfo NO_COPY myself_identity;
+ myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED);
+ }
+
+ SetEvent (wait_sig_inited);
+ sigtid = GetCurrentThreadId ();
+
+ HANDLE catchem[] = {sigcatch_main, sigcatch_nonmain, sigcatch_nosync};
+ sigproc_printf ("Ready. dwProcessid %d", myself->dwProcessId);
+ DWORD rc = RC_NOSYNC;
+ bool flush = false;
+ for (;;)
+ {
+ DWORD i;
+ if (rc == RC_MAIN || rc == RC_NONMAIN)
+ i = RC_NOSYNC;
+ else
+ i = RC_MAIN;
+ rc = WaitForSingleObject (catchem[i], 0);
+ if (rc != WAIT_OBJECT_0)
+ rc = WaitForMultipleObjects (3, catchem, FALSE, sig_loop_wait);
+ else
+ rc = i + WAIT_OBJECT_0;
+ (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY);
+
+ /* 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;
+ sigproc_printf ("awake, rc %d", rc);
+ LONG *todo;
+ if (rc != RC_NOSYNC)
+ todo = todos[0];
+ else
+ todo = todos[1];
+
+ /* A sigcatch semaphore has been signaled. Scan the sigtodo
+ array looking for any unprocessed signals. */
+ pending_signals = false;
+ unsigned more_signals = 0;
+ bool saw_failed_interrupt = false;
+ do
+ {
+ more_signals = 0;
+ for (int sig = -__SIGOFFSET; sig < NSIG; sig++)
+ {
+ LONG x = InterlockedDecrement (todo + sig);
+ if (x < 0)
+ InterlockedIncrement (todo + sig);
+ else if (x >= 0)
+ {
+ /* If x > 0, we have to deal with a signal at some later point */
+ if (rc != RC_NOSYNC && x > 0)
+ /*pending_signals = true*/; // There should be an armed semaphore, in this case
+
+ if (sig > 0 && sig != SIGKILL && sig != SIGSTOP &&
+ (sigismember (&myself->getsigmask (), sig) ||
+ main_vfork->pid ||
+ (sig != SIGCONT && ISSTATE (myself, PID_STOPPED))))
+ {
+ sigproc_printf ("signal %d blocked", sig);
+ x = InterlockedIncrement (myself->getsigtodo (sig));
+ /* pending_signals = true;*/ // will be set by set_process_mask
+ }
+ else
+ {
+ sigproc_printf ("processing signal %d", sig);
+ switch (sig)
+ {
+ case __SIGFLUSH:
+ if (rc == RC_MAIN)
+ {
+ flush = true;
+ SetEvent (sigcatch_nosync);
+ goto out1;
+ }
+ break;
+
+ /* Internal signal to turn on stracing. */
+ case __SIGSTRACE:
+ strace.hello ();
+ break;
+
+ case __SIGCOMMUNE:
+ talktome ();
+ break;
+
+ /* A normal UNIX signal */
+ default:
+ sigproc_printf ("Got signal %d", sig);
+ if (!sig_handle (sig))
+ {
+ pending_signals = true;
+ saw_failed_interrupt = true;
+ x = InterlockedIncrement (myself->getsigtodo (sig));
+ }
+ }
+ if (rc == RC_NOSYNC && x > 0)
+ more_signals++;
+ }
+
+ if (sig == SIGCHLD)
+ proc_subproc (PROC_CLEARWAIT, 0);
+
+ /* Need to take special action if an interrupt failed due to main thread not
+ getting around to calling handler yet. */
+ if (saw_failed_interrupt || rc != RC_NOSYNC)
+ goto out;
+ }
+ }
+#ifdef DEBUGGING
+ if (more_signals > 100)
+ system_printf ("hmm. infinite loop? more_signals %u\n", more_signals);
+#endif
+ }
+ while (more_signals && sig_loop_wait);
+
+ out:
+ /* Signal completion of signal handling depending on which semaphore
+ woke up the WaitForMultipleObjects above. */
+ if (rc == RC_NONMAIN) // FIXME: This is broken
+ ReleaseSemaphore (sigcomplete_nonmain, 1, NULL);
+ else if (rc == RC_MAIN || flush)
+ {
+ SetEvent (sigcomplete_main);
+ sigproc_printf ("set main thread completion event");
+ flush = false;
+ }
+
+ out1:
+ if (saw_failed_interrupt)
+ {
+ SetEvent (sigcatch_nosync);
+ low_priority_sleep (0); /* Hopefully, other thread will be waking up soon. */
+ }
+ sigproc_printf ("looping");
+ }
+
+ sigproc_printf ("done");
+ ExitThread (0);
+}
+
+/* Wait for subprocesses to terminate. Executes in a separate thread. */
+static DWORD WINAPI
+wait_subproc (VOID *)
+{
+ sigproc_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++)
+ proc_subproc (PROC_NOTHING, 0); // Just synchronize and continue
+ if (errloop < 10)
+ continue;
+
+ system_printf ("wait failed. nchildren %d, wait %d, %E",
+ nchildren, proc_loop_wait);
+
+ for (int i = 0; i <= nchildren; i++)
+ if ((rc = WaitForSingleObject (events[i], 0)) == WAIT_OBJECT_0 ||
+ rc == WAIT_TIMEOUT)
+ continue;
+ else if (i == 0)
+ system_printf ("nchildren %d, event[%d] %p, %E", nchildren, i, events[i]);
+ else
+ {
+ system_printf ("nchildren %d, event[%d] %p, pchildren[%d] %p, events[0] %p, %E",
+ nchildren, i, events[i], i - 1, (_pinfo *) pchildren[i - 1], events[0]);
+ system_printf ("pid %d, dwProcessId %u, hProcess %p, progname '%s'",
+ pchildren[i - 1]->pid, pchildren[i - 1]->dwProcessId,
+ pchildren[i - 1]->hProcess, pchildren[i - 1]->progname);
+ }
+ break;
+ }
+
+ errloop = 0;
+ rc -= WAIT_OBJECT_0;
+ if (rc-- != 0)
+ {
+ rc = proc_subproc (PROC_CHILDTERMINATED, rc);
+ if (!proc_loop_wait) // Don't bother if wait_subproc is
+ break; // exiting
+
+ /* 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. */
+ if (rc)
+ sig_send (myself_nowait, SIGCHLD);
+ }
+ sigproc_printf ("looping");
+ }
+
+ ForceCloseHandle (events[0]);
+ events[0] = NULL;
+ sigproc_printf ("done");
+ ExitThread (0);
+}
+
+extern "C" {
+/* Provide a stack frame when calling WaitFor* functions */
+
+#undef WaitForSingleObject
+
+DWORD __stdcall
+WFSO (HANDLE hHandle, DWORD dwMilliseconds)
+{
+ DWORD ret;
+ sigframe thisframe (mainthread);
+ ret = WaitForSingleObject (hHandle, dwMilliseconds);
+ return ret;
+}
+
+#undef WaitForMultipleObjects
+
+DWORD __stdcall
+WFMO (DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds)
+{
+ DWORD ret;
+ sigframe thisframe (mainthread);
+ ret = WaitForMultipleObjects (nCount, lpHandles, fWaitAll, dwMilliseconds);
+ return ret;
+}
+}