diff options
Diffstat (limited to 'winsup/cygwin/sigproc.cc')
-rw-r--r-- | winsup/cygwin/sigproc.cc | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc new file mode 100644 index 00000000000..1ddb74e4ef1 --- /dev/null +++ b/winsup/cygwin/sigproc.cc @@ -0,0 +1,1045 @@ +/* sigproc.cc: inter/intra signal and sub process handler + + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + + Written by Christopher Faylor + +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 "pinfo.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "child_info_magic.h" +#include "shared_info.h" +#include "cygthread.h" +#include "cygtls.h" +#include "sigproc.h" +#include "perthread.h" +#include "exceptions.h" + +/* + * Convenience defines + */ +#define WSSC 60000 // Wait for signal completion +#define WPSP 40000 // Wait for proc_subproc mutex + +#define PSIZE 63 // Number of processes + +#define no_signals_available() (!hwait_sig || (myself->sendsig == INVALID_HANDLE_VALUE) || exit_state) + +#define NPROCS 256 + +/* + * Global variables + */ +struct sigaction *global_sigs; + +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 +HANDLE NO_COPY signal_arrived; // Event signaled when a signal has + // resulted in a user-specified + // function call + +#define Static static NO_COPY + +HANDLE NO_COPY sigCONT; // Used to "STOP" a process +Static cygthread *hwait_sig; // Handle of wait_sig thread + +Static HANDLE wait_sig_inited; // Control synchronization of + // message queue startup + +Static int nprocs; // Number of deceased children +Static char cprocs[(NPROCS + 1) * sizeof (pinfo)]; // All my deceased children info +#define procs ((pinfo *) cprocs) +Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads + +muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff + +DWORD NO_COPY sigtid = 0; // ID of the signal thread + +/* Function declarations */ +static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1))); +static __inline__ bool get_proc_lock (DWORD, DWORD); +static void __stdcall remove_proc (int); +static bool __stdcall stopped_or_terminated (waitq *, _pinfo *); +static DWORD WINAPI wait_sig (VOID *arg); + +/* wait_sig bookkeeping */ + +class pending_signals +{ + sigpacket sigs[NSIG + 1]; + sigpacket start; + sigpacket *end; + sigpacket *prev; + sigpacket *curr; +public: + void reset () {curr = &start; prev = &start;} + void add (sigpacket&); + void del (); + sigpacket *next (); + sigpacket *save () const {return curr;} + void restore (sigpacket *saved) {curr = saved;} + friend void __stdcall sig_dispatch_pending (bool); + friend DWORD WINAPI wait_sig (VOID *arg); +}; + +static pending_signals sigq; + +/* Functions */ +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++) + { + global_sigs[i].sa_mask = 0; + if (global_sigs[i].sa_handler != SIG_IGN) + global_sigs[i].sa_handler = SIG_DFL; + } +} + +/* Determine if the parent process is alive. + */ + +bool __stdcall +my_parent_is_alive () +{ + bool res; + if (myself->cygstarted) + res = pid_exists (myself->ppid); + else + { + debug_printf ("Not started by cygwin app"); + res = false; + } + 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, proc 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) + { + sigproc_printf ("sync_proc_subproc is NULL (1)"); + return false; + } + if (sync_proc_subproc->acquire (WPSP)) + { + lastwhat = what; + return true; + } + if (!sync_proc_subproc) + { + sigproc_printf ("sync_proc_subproc is NULL (2)"); + return false; + } + system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), last %d, %E", + what, val, lastwhat); + return true; +} + +static bool __stdcall +proc_can_be_signalled (_pinfo *p) +{ + if (p->sendsig == INVALID_HANDLE_VALUE) + { + set_errno (EPERM); + return false; + } + + if (p == myself_nowait || p == myself) + return hwait_sig; + + if (ISSTATE (p, PID_INITIALIZING) || + (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) == + (PID_ACTIVE | PID_IN_USE))) + return true; + + set_errno (ESRCH); + return false; +} + +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 | PID_ZOMBIE)); +} + +/* Return 1 if this is one of our children, zero otherwise. + FIXME: This really should be integrated with the rest of the proc_subproc + testing. Scanning these lists twice is inefficient. */ +bool __stdcall +mychild (int pid) +{ + pinfo p (pid); + return p && p->ppid == myself->pid; +} + +/* 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. what %d, val %d", what, val); + goto out1; + } + + switch (what) + { + /* Add a new subprocess to the children arrays. + * (usually called from the main thread) + */ + case PROC_ADDCHILD: + /* Filled up process table? */ + if (nprocs >= NPROCS) + { + sigproc_printf ("proc table overflow: hit %d processes, pid %d\n", + nprocs, vchild->pid); + rc = 0; + set_errno (EMFILE); // FIXMENOW - what's the right errno? + break; + } + + vchild->ppid = myself->pid; + vchild->uid = myself->uid; + vchild->gid = myself->gid; + vchild->pgid = myself->pgid; + vchild->sid = myself->sid; + vchild->ctty = myself->ctty; + vchild->cygstarted = true; + vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + procs[nprocs] = vchild; + rc = procs[nprocs].wait (); + if (rc) + { + sigproc_printf ("added pid %d to proc table, slot %d", vchild->pid, + nprocs); + nprocs++; + } + 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); + ProtectHandle1 (wval->ev, wq_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"); + } + + // FIXMENOW: What is supposed to happen here? + if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN) + while (nprocs) + remove_proc (0); + break; + } + +out: + sync_proc_subproc->release (); // Release the lock +out1: + sigproc_printf ("returning %d", rc); + return rc; +} + +// FIXME: This is inelegant +void +_cygtls::remove_wq (DWORD wait) +{ + if (sync_proc_subproc && sync_proc_subproc->acquire (wait)) + { + for (waitq *w = &waitq_head; w->next != NULL; w = w->next) + if (w->next == &wq) + { + ForceCloseHandle1 (wq.thread_ev, wq_ev); + w->next = wq.next; + break; + } + sync_proc_subproc->release (); + } +} + +/* 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 procs. + */ +void __stdcall +proc_terminate (void) +{ + sigproc_printf ("nprocs %d", nprocs); + /* Signal processing is assumed to be blocked in this routine. */ + if (nprocs) + { + sync_proc_subproc->acquire (WPSP); + + (void) proc_subproc (PROC_CLEARWAIT, 1); + + /* Clean out proc processes from the pid list. */ + int i; + for (i = 0; i < nprocs; i++) + { + procs[i]->ppid = 1; + if (!proc_exists (procs[i])) + procs[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */ + procs[i].release (); + } + nprocs = 0; + sync_proc_subproc->release (); + } + sigproc_printf ("leaving"); +} + +/* Clear pending signal */ +void __stdcall +sig_clear (int target_sig) +{ + if (GetCurrentThreadId () != sigtid) + sig_send (myself, -target_sig); + else + { + sigpacket *q; + sigpacket *save = sigq.save (); + sigq.reset (); + while ((q = sigq.next ())) + if (q->si.si_signo == target_sig) + { + q->si.si_signo = __SIGDELETE; + break; + } + sigq.restore (save); + } + return; +} + +extern "C" int +sigpending (sigset_t *mask) +{ + sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING); + if (outset == SIG_BAD_MASK) + return -1; + *mask = outset; + return 0; +} + +/* Force the wait_sig thread to wake up and scan for pending signals */ +void __stdcall +sig_dispatch_pending (bool fast) +{ + if (exit_state || GetCurrentThreadId () == sigtid || !sigq.start.next) + { +#ifdef DEBUGGING + sigproc_printf ("exit_state %d, cur thread id %p, sigtid %p, sigq.start.next %p", + exit_state, GetCurrentThreadId (), sigtid, sigq.start.next); +#endif + return; + } + +#ifdef DEBUGGING + sigproc_printf ("flushing"); +#endif + (void) sig_send (myself, fast ? __SIGFLUSHFAST : __SIGFLUSH); +} + +/* 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 proc 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 (); + + global_sigs[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) +{ + extern HANDLE hExeced; + hwait_sig = NULL; + + if (myself->sendsig == INVALID_HANDLE_VALUE) + sigproc_printf ("sigproc handling not active"); + else + { + sigproc_printf ("entering"); + // finished with anything it is doing + if (!hExeced) + { + HANDLE sendsig = myself->sendsig; + myself->sendsig = INVALID_HANDLE_VALUE; + CloseHandle (sendsig); + } + } + proc_terminate (); // Terminate process handling thread + + return; +} + +int __stdcall +sig_send (_pinfo *p, int sig) +{ + siginfo_t si; + si.si_signo = sig; + si.si_code = SI_KERNEL; + si.si_pid = si.si_uid = si.si_errno = 0; + return sig_send (p, si); +} + +/* 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, siginfo_t& si, _cygtls *tls) +{ + int rc = 1; + bool its_me; + HANDLE sendsig; + sigpacket pack; + + pack.wakeup = NULL; + bool wait_for_completion; + if (!(its_me = (p == NULL || p == myself || p == myself_nowait))) + wait_for_completion = false; + else + { + if (no_signals_available ()) + { + sigproc_printf ("hwait_sig %p, myself->sendsig %p, exit_state %d", + hwait_sig, myself->sendsig, exit_state); + goto out; // Either exiting or not yet initializing + } + if (wait_sig_inited) + wait_for_sigthread (); + wait_for_completion = p != myself_nowait && _my_tls.isinitialized (); + 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, si.si_signo); + goto out; + } + + if (its_me) + sendsig = myself->sendsig; + else + { + for (int i = 0; !p->dwProcessId && i < 10000; i++) + low_priority_sleep (0); + HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId); + if (!hp) + { + sigproc_printf ("OpenProcess failed, %E"); + __seterrno (); + goto out; + } + VerifyHandle (hp); + for (int i = 0; !p->sendsig && i < 10000; i++) + low_priority_sleep (0); + if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0, + DUPLICATE_SAME_ACCESS) || !sendsig) + { + CloseHandle (hp); + sigproc_printf ("DuplicateHandle failed, %E"); + __seterrno (); + goto out; + } + CloseHandle (hp); + VerifyHandle (sendsig); + } + + sigproc_printf ("sendsig %p, pid %d, signal %d, its_me %d", sendsig, p->pid, si.si_signo, its_me); + + sigset_t pending; + if (!its_me) + pack.mask = NULL; + else if (si.si_signo == __SIGPENDING) + pack.mask = &pending; + else if (si.si_signo == __SIGFLUSH || si.si_signo > 0) + pack.mask = &myself->getsigmask (); + else + pack.mask = NULL; + + pack.si = si; + if (!pack.si.si_pid) + pack.si.si_pid = myself->pid; + if (!pack.si.si_uid) + pack.si.si_uid = myself->uid; + pack.pid = myself->pid; + pack.tls = (_cygtls *) tls; + if (wait_for_completion) + { + pack.wakeup = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + sigproc_printf ("wakeup %p", pack.wakeup); + ProtectHandle (pack.wakeup); + } + + DWORD nb; + if (!WriteFile (sendsig, &pack, sizeof (pack), &nb, NULL) || nb != sizeof (pack)) + { + /* Couldn't send to the pipe. This probably means that the + process is exiting. */ + if (!its_me) + { + sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig); + __seterrno (); + ForceCloseHandle (sendsig); + } + else + { + if (no_signals_available ()) + sigproc_printf ("I'm going away now"); + else + system_printf ("error sending signal %d to pid %d, pipe handle %p, %E", + si.si_signo, p->pid, sendsig); + } + 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) + { + sigproc_printf ("Waiting for pack.wakeup %p", pack.wakeup); + rc = WaitForSingleObject (pack.wakeup, WSSC); + } + else + { + rc = WAIT_OBJECT_0; + sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d", + its_me, si.si_signo); + if (!its_me) + ForceCloseHandle (sendsig); + } + + if (pack.wakeup) + { + ForceCloseHandle (pack.wakeup); + pack.wakeup = NULL; + } + + if (rc == WAIT_OBJECT_0) + rc = 0; // Successful exit + else + { + if (!no_signals_available ()) + system_printf ("wait for sig_complete event failed, signal %d, rc %d, %E", + si.si_signo, rc); + set_errno (ENOSYS); + rc = -1; + } + + if (wait_for_completion && si.si_signo != __SIGFLUSHFAST) + _my_tls.call_signal_handler (); + +out: + if (pack.wakeup) + ForceCloseHandle (pack.wakeup); + if (si.si_signo != __SIGPENDING) + /* nothing */; + else if (!rc) + rc = (int) pending; + else + rc = SIG_BAD_MASK; + sigproc_printf ("returning %p from sending signal %d", rc, si.si_signo); + return rc; +} + +/* Initialize some of the memory block passed to child processes + by fork/spawn/exec. */ + +void __stdcall +init_child_info (DWORD chtype, child_info *ch, HANDLE subproc_ready) +{ + memset (ch, 0, sizeof *ch); + ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info); + ch->intro = PROC_MAGIC_GENERIC; + ch->magic = CHILD_INFO_MAGIC; + ch->type = chtype; + ch->subproc_ready = subproc_ready; + ch->fhandler_union_cb = sizeof (fhandler_union); + ch->user_h = cygwin_user_h; +} + +/* 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 ("nprocs %d", nprocs); + + /* Check already dead processes first to see if they match the criteria + * given in w->next. */ + int res; + for (int i = 0; i < nprocs; i++) + if ((res = stopped_or_terminated (parent_w, procs[i]))) + { + remove_proc (i); + potential_match = 1; + goto out; + } + + potential_match = -!!nprocs; + +out: + sigproc_printf ("returning %d", potential_match); + return potential_match; +} + +/* Remove a proc from procs by swapping it with the last child in the list. + Also releases shared memory of exited processes. */ +static void __stdcall +remove_proc (int ci) +{ + if (!proc_exists (procs[ci])) + { + sigproc_printf ("removing procs[%d], pid %d, nprocs %d", ci, procs[ci]->pid, + nprocs); + procs[ci].release (); + if (ci < --nprocs) + procs[ci] = procs[nprocs]; + } +} + +/* 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 non-zero if waiting thread released. */ +static bool __stdcall +stopped_or_terminated (waitq *parent_w, _pinfo *child) +{ + int might_match; + waitq *w = parent_w->next; + + sigproc_printf ("considering pid %d", child->pid); + if (w->pid == -1) + might_match = 1; + else if (w->pid == 0) + might_match = child->pgid == myself->pgid; + else if (w->pid < 0) + might_match = child->pgid == -w->pid; + else + might_match = (w->pid == child->pid); + + if (!might_match) + return 0; + + int terminated; + + if (!((terminated = child->process_state == PID_ZOMBIE) || + ((w->options & WUNTRACED) && child->stopsig))) + return 0; + + 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 procs array */ + { + w->status = child->exitcode; + + 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 true; +} + +static void +talktome () +{ + winpids pids ((DWORD) PID_MAP_RW); + for (unsigned i = 0; i < pids.npids; i++) + if (pids[i]->hello_pid == myself->pid) + if (!IsBadWritePtr (pids[i], sizeof (_pinfo))) + pids[i]->commune_recv (); +} + +void +pending_signals::add (sigpacket& pack) +{ + sigpacket *se; + if (sigs[pack.si.si_signo].si.si_signo) + return; + se = sigs + pack.si.si_signo; + *se = pack; + se->mask = &myself->getsigmask (); + se->next = NULL; + if (end) + end->next = se; + end = se; + if (!start.next) + start.next = se; +} + +void +pending_signals::del () +{ + sigpacket *next = curr->next; + prev->next = next; + curr->si.si_signo = 0; +#ifdef DEBUGGING + curr->next = NULL; +#endif + if (end == curr) + end = prev; + curr = next; +} + +sigpacket * +pending_signals::next () +{ + sigpacket *res; + prev = curr; + if (!curr || !(curr = curr->next)) + res = NULL; + else + res = curr; + return res; +} + +/* Process signals by waiting for signal data to arrive in a pipe. + Set a completion event if one was specified. */ +static DWORD WINAPI +wait_sig (VOID *self) +{ + HANDLE readsig; + char sa_buf[1024]; + Static bool holding_signals; + + /* Initialization */ + (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); + + if (!CreatePipe (&readsig, &myself->sendsig, sec_user_nih (sa_buf), 0)) + api_fatal ("couldn't create signal pipe, %E"); + 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; + + sigproc_printf ("myself->dwProcessId %u", myself->dwProcessId); +#if 0 + /* 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); + } +#endif + + SetEvent (wait_sig_inited); + sigtid = GetCurrentThreadId (); + + exception_list el; + _my_tls.init_threadlist_exceptions (&el); + debug_printf ("entering ReadFile loop, readsig %p, myself->sendsig %p", + readsig, myself->sendsig); + + for (;;) + { + DWORD nb; + sigpacket pack; + if (!ReadFile (readsig, &pack, sizeof (pack), &nb, NULL)) + break; + if (myself->sendsig == INVALID_HANDLE_VALUE) + break; + + if (nb != sizeof (pack)) + { + system_printf ("short read from signal pipe: %d != %d", nb, + sizeof (pack)); + continue; + } + + if (!pack.si.si_signo) + { +#ifdef DEBUGGING + system_printf ("zero signal?"); +#endif + continue; + } + + sigset_t dummy_mask; + if (!pack.mask) + { + dummy_mask = myself->getsigmask (); + pack.mask = &dummy_mask; + } + + sigpacket *q; + bool clearwait = false; + switch (pack.si.si_signo) + { + case __SIGCOMMUNE: + talktome (); + break; + case __SIGSTRACE: + strace.hello (); + break; + case __SIGPENDING: + *pack.mask = 0; + unsigned bit; + sigq.reset (); + while ((q = sigq.next ())) + if (myself->getsigmask () & (bit = SIGTOMASK (q->si.si_signo))) + *pack.mask |= bit; + break; + case __SIGHOLD: + holding_signals = 1; + break; + case __SIGNOHOLD: + holding_signals = 0; + /* fall through, intentionally */ + case __SIGFLUSH: + case __SIGFLUSHFAST: + sigq.reset (); + while ((q = sigq.next ())) + { + int sig = q->si.si_signo; + if (sig == __SIGDELETE || q->process () > 0) + sigq.del (); + if (sig == __SIGNOHOLD && q->si.si_signo == SIGCHLD) + clearwait = true; + } + break; + default: + if (pack.si.si_signo < 0) + sig_clear (-pack.si.si_signo); + else if (holding_signals) + sigq.add (pack); + else + { + int sig = pack.si.si_signo; + // FIXME: Not quite right when taking threads into consideration. + // Do we need a per-thread queue? + if (sigq.sigs[sig].si.si_signo) + sigproc_printf ("sig %d already queued", pack.si.si_signo); + else + { + int sigres = pack.process (); + if (sigres <= 0) + { +#ifdef DEBUGGING2 + if (!sigres) + system_printf ("Failed to arm signal %d from pid %d", pack.sig, pack.pid); +#endif + sigq.add (pack); // FIXME: Shouldn't add this in !sh condition + } + } + if (sig == SIGCHLD) + clearwait = true; + } + break; + } + if (clearwait) + proc_subproc (PROC_CLEARWAIT, 0); + if (pack.wakeup) + { + SetEvent (pack.wakeup); + sigproc_printf ("signalled %p", pack.wakeup); + } + } + + sigproc_printf ("done"); + ExitThread (0); +} |