diff options
Diffstat (limited to 'winsup/cygwin/sigproc.cc')
-rw-r--r-- | winsup/cygwin/sigproc.cc | 1362 |
1 files changed, 0 insertions, 1362 deletions
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc deleted file mode 100644 index 12f61d2fb..000000000 --- a/winsup/cygwin/sigproc.cc +++ /dev/null @@ -1,1362 +0,0 @@ -/* sigproc.cc: inter/intra signal and sub process handler - - Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. - -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 "miscfuncs.h" -#include <stdlib.h> -#include <sys/cygwin.h> -#include "cygerrno.h" -#include "sigproc.h" -#include "path.h" -#include "fhandler.h" -#include "dtable.h" -#include "cygheap.h" -#include "child_info_magic.h" -#include "shared_info.h" -#include "cygtls.h" -#include "ntdll.h" -#include "exception.h" - -/* - * Convenience defines - */ -#define WSSC 60000 // Wait for signal completion -#define WPSP 40000 // Wait for proc_subproc mutex - -/* - * Global variables - */ -struct sigaction *global_sigs; - -const char *__sp_fn ; -int __sp_ln; - -bool no_thread_exit_protect::flag; - -char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to - // current process but no wait is required - -#define Static static NO_COPY - - -Static int nprocs; // Number of deceased children -Static char cprocs[(NPROCS + 1) * sizeof (pinfo)];// All my children info -#define procs ((pinfo *) cprocs) // All this just to avoid expensive - // constructor operation at DLL startup -Static waitq waitq_head; // Start of queue for wait'ing threads - -Static muto sync_proc_subproc; // Control access to subproc stuff - -_cygtls NO_COPY *_sig_tls; - -Static HANDLE my_sendsig; -Static HANDLE my_readsig; - -/* Function declarations */ -static int __reg1 checkstate (waitq *); -static __inline__ bool get_proc_lock (DWORD, DWORD); -static bool __stdcall remove_proc (int); -static bool __stdcall stopped_or_terminated (waitq *, _pinfo *); -static void WINAPI wait_sig (VOID *arg); - -/* wait_sig bookkeeping */ - -class pending_signals -{ - sigpacket sigs[NSIG + 1]; - sigpacket start; - bool retry; - -public: - void add (sigpacket&); - bool pending () {retry = true; return !!start.next;} - void clear (int sig) {sigs[sig].si.si_signo = 0;} - friend void __reg1 sig_dispatch_pending (bool);; - friend void WINAPI wait_sig (VOID *arg); - friend void sigproc_init (); -}; - -Static pending_signals sigq; - -/* Functions */ -void __stdcall -sigalloc () -{ - cygheap->sigs = global_sigs = - (struct sigaction *) ccalloc_abort (HEAP_SIGS, NSIG, sizeof (struct sigaction)); - global_sigs[SIGSTOP].sa_flags = SA_RESTART | SA_NODEFER; -} - -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; - global_sigs[i].sa_flags &= ~ SA_SIGINFO; - } - } -} - -/* 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) -{ - if (!cygwin_finished_initializing) - return true; - Static int lastwhat = -1; - if (!sync_proc_subproc) - { - sigproc_printf ("sync_proc_subproc is NULL"); - return false; - } - if (sync_proc_subproc.acquire (WPSP)) - { - lastwhat = what; - return true; - } - system_printf ("Couldn't acquire %s for(%d,%d), last %d, %E", - sync_proc_subproc.name, what, val, lastwhat); - return false; -} - -static bool __stdcall -proc_can_be_signalled (_pinfo *p) -{ - if (!(p->exitcode & EXITCODE_SET)) - { - 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 __reg1 -pid_exists (pid_t pid) -{ - return pinfo (pid)->exists (); -} - -/* Return true if this is one of our children, false otherwise. */ -static inline bool __stdcall -mychild (int pid) -{ - for (int i = 0; i < nprocs; i++) - if (procs[i]->pid == pid) - return true; - return false; -} - -/* Handle all subprocess requests - */ -int __reg2 -proc_subproc (DWORD what, uintptr_t val) -{ - int rc = 1; - int potential_match; - int clearing; - waitq *w; - -#define wval ((waitq *) val) -#define vchild (*((pinfo *) 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 (EAGAIN); - break; - } - /* fall through intentionally */ - - case PROC_DETACHED_CHILD: - if (vchild != myself) - { - 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; - vchild->ppid = what == PROC_DETACHED_CHILD ? 1 : myself->pid; /* always set last */ - } - if (what == PROC_DETACHED_CHILD) - break; - /* fall through intentionally */ - - case PROC_REATTACH_CHILD: - 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 != -1 && wval->pid && !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 = false; - goto scan_wait; - - case PROC_EXEC_CLEANUP: - while (nprocs) - remove_proc (0); - for (w = &waitq_head; w->next != NULL; w = w->next) - CloseHandle (w->next->ev); - break; - - /* 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, nprocs %d", nprocs); - 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"); - } - - if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN) - for (int i = 0; i < nprocs; i += remove_proc (i)) - continue; - } - -out: - sync_proc_subproc.release (); // Release the lock -out1: - sigproc_printf ("returning %d", rc); - return rc; -#undef wval -#undef vchild -} - -// FIXME: This is inelegant -void -_cygtls::remove_wq (DWORD wait) -{ - if (wq.thread_ev) - { - if (exit_state < ES_FINAL && waitq_head.next && sync_proc_subproc - && sync_proc_subproc.acquire (wait)) - { - ForceCloseHandle1 (wq.thread_ev, wq_ev); - wq.thread_ev = NULL; - for (waitq *w = &waitq_head; w->next != NULL; w = w->next) - if (w->next == &wq) - { - 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 () -{ - sigproc_printf ("nprocs %d", nprocs); - if (nprocs) - { - sync_proc_subproc.acquire (WPSP); - - proc_subproc (PROC_CLEARWAIT, 1); - - /* Clean out proc processes from the pid list. */ - for (int i = 0; i < nprocs; i++) - { - /* If we've execed then the execed process will handle setting ppid - to 1 iff it is a Cygwin process. */ - if (!have_execed || !have_execed_cygwin) - procs[i]->ppid = 1; - if (procs[i].wait_thread) - procs[i].wait_thread->terminate_thread (); - /* Release memory associated with this process unless it is 'myself'. - 'myself' is only in the procs table when we've execed. We reach - here when the next process has finished initializing but we still - can't free the memory used by 'myself' since it is used later on - during cygwin tear down. */ - if (procs[i] != myself) - procs[i].release (); - } - nprocs = 0; - sync_proc_subproc.release (); - } - sigproc_printf ("leaving"); -} - -/* Clear pending signal */ -void __reg1 -sig_clear (int sig) -{ - sigq.clear (sig); -} - -extern "C" int -sigpending (sigset_t *mask) -{ - sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING, &_my_tls); - 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 __reg1 -sig_dispatch_pending (bool fast) -{ - /* Non-atomically test for any signals pending and wake up wait_sig if any are - found. It's ok if there's a race here since the next call to this function - should catch it. */ - if (sigq.pending () && &_my_tls != _sig_tls) - sig_send (myself, fast ? __SIGFLUSHFAST : __SIGFLUSH); -} - -/* Signal thread initialization. Called from dll_crt0_1. - This routine starts the signal handling thread. */ -void __stdcall -sigproc_init () -{ - char char_sa_buf[1024]; - PSECURITY_ATTRIBUTES sa = sec_user_nih ((PSECURITY_ATTRIBUTES) char_sa_buf, cygheap->user.sid()); - DWORD err = fhandler_pipe::create (sa, &my_readsig, &my_sendsig, - NSIG * sizeof (sigpacket), "sigwait", - PIPE_ADD_PID); - if (err) - { - SetLastError (err); - api_fatal ("couldn't create signal pipe, %E"); - } - ProtectHandle (my_readsig); - myself->sendsig = my_sendsig; - /* sync_proc_subproc is used by proc_subproc. It serializes - access to the children and proc arrays. */ - sync_proc_subproc.init ("sync_proc_subproc"); - new cygthread (wait_sig, cygself, "sig"); -} - -/* Exit the current thread very carefully. - See cgf-000017 in DevNotes for more details on why this is - necessary. */ -void -exit_thread (DWORD res) -{ -# undef ExitThread - if (no_thread_exit_protect ()) - ExitThread (res); - sigfillset (&_my_tls.sigmask); /* No signals wanted */ - - /* CV 2014-11-21: Disable the code sending a signal. The problem with - this code is that it allows deadlocks under signal-rich multithreading - conditions. - The original problem reported in 2012 couldn't be reproduced anymore, - even disabling this code. Tested on XP 32, Vista 32, W7 32, WOW64, 64, - W8.1 WOW64, 64. */ -#if 0 - lock_process for_now; /* May block indefinitely when exiting. */ - HANDLE h; - if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), - GetCurrentProcess (), &h, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { -#ifdef DEBUGGING - system_printf ("couldn't duplicate the current thread, %E"); -#endif - for_now.release (); - ExitThread (res); - } - ProtectHandle1 (h, exit_thread); - /* Tell wait_sig to wait for this thread to exit. It can then release - the lock below and close the above-opened handle. */ - siginfo_t si = {__SIGTHREADEXIT, SI_KERNEL}; - si.si_cyg = h; - sig_send (myself_nowait, si, &_my_tls); -#endif - ExitThread (res); -} - -int __reg3 -sig_send (_pinfo *p, int sig, _cygtls *tls) -{ - siginfo_t si = {}; - si.si_signo = sig; - si.si_code = SI_KERNEL; - return sig_send (p, si, tls); -} - -/* 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 __reg3 -sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) -{ - int rc = 1; - bool its_me; - HANDLE sendsig; - sigpacket pack; - bool communing = si.si_signo == __SIGCOMMUNE; - - pack.wakeup = NULL; - bool wait_for_completion; - if (!(its_me = p == NULL || p == myself || p == myself_nowait)) - { - /* 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; - } - wait_for_completion = false; - } - else - { - wait_for_completion = p != myself_nowait; - p = myself; - } - - - if (its_me) - sendsig = my_sendsig; - else - { - HANDLE dupsig; - DWORD dwProcessId; - for (int i = 0; !p->sendsig && i < 10000; i++) - yield (); - if (p->sendsig) - { - dupsig = p->sendsig; - dwProcessId = p->dwProcessId; - } - else - { - dupsig = p->exec_sendsig; - dwProcessId = p->exec_dwProcessId; - } - if (!dupsig) - { - set_errno (EAGAIN); - sigproc_printf ("sendsig handle never materialized"); - goto out; - } - HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId); - if (!hp) - { - __seterrno (); - sigproc_printf ("OpenProcess failed, %E"); - goto out; - } - VerifyHandle (hp); - if (!DuplicateHandle (hp, dupsig, GetCurrentProcess (), &sendsig, 0, - false, DUPLICATE_SAME_ACCESS) || !sendsig) - { - __seterrno (); - sigproc_printf ("DuplicateHandle failed, %E"); - CloseHandle (hp); - goto out; - } - VerifyHandle (sendsig); - if (!communing) - { - CloseHandle (hp); - DWORD flag = PIPE_NOWAIT; - /* Set PIPE_NOWAIT here to avoid blocking when sending a signal. - (Yes, I know MSDN says not to use this) - We can't ever block here because it causes a deadlock when - debugging with gdb. */ - BOOL res = SetNamedPipeHandleState (sendsig, &flag, NULL, NULL); - sigproc_printf ("%d = SetNamedPipeHandleState (%y, PIPE_NOWAIT, NULL, NULL)", res, sendsig); - } - else - { - si._si_commune._si_process_handle = hp; - - HANDLE& tome = si._si_commune._si_write_handle; - HANDLE& fromthem = si._si_commune._si_read_handle; - if (!CreatePipeOverlapped (&fromthem, &tome, &sec_all_nih)) - { - sigproc_printf ("CreatePipe for __SIGCOMMUNE failed, %E"); - __seterrno (); - goto out; - } - if (!DuplicateHandle (GetCurrentProcess (), tome, hp, &tome, 0, false, - DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) - { - sigproc_printf ("DuplicateHandle for __SIGCOMMUNE failed, %E"); - __seterrno (); - goto out; - } - } - } - - 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) - { - threadlist_t *tl_entry = cygheap->find_tls (tls ? tls : _main_tls); - pack.mask = tls ? &tls->sigmask : &_main_tls->sigmask; - cygheap->unlock_tls (tl_entry); - } - 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.sigtls = tls; - if (wait_for_completion) - { - pack.wakeup = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); - sigproc_printf ("wakeup %p", pack.wakeup); - ProtectHandle (pack.wakeup); - } - - char *leader; - size_t packsize; - if (!communing || !(si._si_commune._si_code & PICOM_EXTRASTR)) - { - leader = (char *) &pack; - packsize = sizeof (pack); - } - else - { - size_t n = strlen (si._si_commune._si_str); - packsize = sizeof (pack) + sizeof (n) + n; - char *p = leader = (char *) alloca (packsize); - memcpy (p, &pack, sizeof (pack)); p += sizeof (pack); - memcpy (p, &n, sizeof (n)); p += sizeof (n); - memcpy (p, si._si_commune._si_str, n); p += n; - } - - DWORD nb; - BOOL res; - /* Try multiple times to send if packsize != nb since that probably - means that the pipe buffer is full. */ - for (int i = 0; i < 100; i++) - { - res = WriteFile (sendsig, leader, packsize, &nb, NULL); - if (!res || packsize == nb) - break; - Sleep (10); - res = 0; - } - - if (!res) - { - /* 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); - ForceCloseHandle (sendsig); - } - else if (!p->exec_sendsig && !exit_state) - system_printf ("error sending signal %d, pid %u, pipe handle %p, nb %u, packsize %u, %E", - si.si_signo, p->pid, sendsig, nb, packsize); - if (GetLastError () == ERROR_BROKEN_PIPE) - set_errno (ESRCH); - else - __seterrno (); - 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); - ForceCloseHandle (pack.wakeup); - } - 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); - } - - pack.wakeup = NULL; - if (rc == WAIT_OBJECT_0) - rc = 0; // Successful exit - else - { - set_errno (ENOSYS); - rc = -1; - } - - if (wait_for_completion && si.si_signo != __SIGFLUSHFAST) - _my_tls.call_signal_handler (); - -out: - if (communing && rc) - { - if (si._si_commune._si_process_handle) - CloseHandle (si._si_commune._si_process_handle); - if (si._si_commune._si_read_handle) - CloseHandle (si._si_commune._si_read_handle); - } - 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; -} - -int child_info::retry_count = 0; - -/* Initialize some of the memory block passed to child processes - by fork/spawn/exec. */ -child_info::child_info (unsigned in_cb, child_info_types chtype, - bool need_subproc_ready): - cb (in_cb), intro (PROC_MAGIC_GENERIC), magic (CHILD_INFO_MAGIC), - type (chtype), cygheap (::cygheap), cygheap_max (::cygheap_max), - flag (0), retry (child_info::retry_count), rd_proc_pipe (NULL), - wr_proc_pipe (NULL) -{ - /* It appears that when running under WOW64 on Vista 64, the first DWORD - value in the datastructure lpReserved2 is pointing to (msv_count in - Cygwin), has to reflect the size of that datastructure as used in the - Microsoft C runtime (a count value, counting the number of elements in - two subsequent arrays, BYTE[count and HANDLE[count]), even though the C - runtime isn't used. Otherwise, if msv_count is 0 or too small, the - datastructure gets overwritten. - - This seems to be a bug in Vista's WOW64, which apparently copies the - lpReserved2 datastructure not using the cbReserved2 size information, - but using the information given in the first DWORD within lpReserved2 - instead. 32 bit Windows and former WOW64 don't care if msv_count is 0 - or a sensible non-0 count value. However, it's not clear if a non-0 - count doesn't result in trying to evaluate the content, so we do this - really only for Vista 64 for now. - - Note: It turns out that a non-zero value *does* harm operation on - XP 64 and 2K3 64 (Crash in CreateProcess call). - - The value is sizeof (child_info_*) / 5 which results in a count which - covers the full datastructure, plus not more than 4 extra bytes. This - is ok as long as the child_info structure is cosily stored within a bigger - datastructure. */ - msv_count = wincap.needs_count_in_si_lpres2 () ? in_cb / 5 : 0; - - fhandler_union_cb = sizeof (fhandler_union); - user_h = cygwin_user_h; - if (strace.active ()) - flag |= _CI_STRACED; - if (need_subproc_ready) - { - subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL); - flag |= _CI_ISCYGWIN; - } - sigproc_printf ("subproc_ready %p", subproc_ready); - /* Create an inheritable handle to pass to the child process. This will - allow the child to duplicate handles from the parent to itself. */ - parent = NULL; - if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (), - GetCurrentProcess (), &parent, 0, true, - DUPLICATE_SAME_ACCESS)) - system_printf ("couldn't create handle to myself for child, %E"); -} - -child_info::~child_info () -{ - cleanup (); -} - -child_info_fork::child_info_fork () : - child_info (sizeof *this, _CH_FORK, true), - forker_finished (NULL) -{ -} - -child_info_spawn::child_info_spawn (child_info_types chtype, bool need_subproc_ready) : - child_info (sizeof *this, chtype, need_subproc_ready) -{ - if (type == _CH_EXEC) - { - hExeced = NULL; - if (my_wr_proc_pipe) - ev = NULL; - else if (!(ev = CreateEvent (&sec_none_nih, false, false, NULL))) - api_fatal ("couldn't create signalling event for exec, %E"); - - get_proc_lock (PROC_EXECING, 0); - /* exit with lock held */ - } -} - -cygheap_exec_info * -cygheap_exec_info::alloc () -{ - cygheap_exec_info *res = - (cygheap_exec_info *) ccalloc_abort (HEAP_1_EXEC, 1, - sizeof (cygheap_exec_info) - + (nprocs * sizeof (children[0]))); - res->sigmask = _my_tls.sigmask; - return res; -} - -void -child_info_spawn::wait_for_myself () -{ - postfork (myself); - myself.remember (false); - WaitForSingleObject (ev, INFINITE); -} - -void -child_info::cleanup () -{ - if (subproc_ready) - { - CloseHandle (subproc_ready); - subproc_ready = NULL; - } - if (parent) - { - CloseHandle (parent); - parent = NULL; - } - if (rd_proc_pipe) - { - ForceCloseHandle (rd_proc_pipe); - rd_proc_pipe = NULL; - } - if (wr_proc_pipe) - { - ForceCloseHandle (wr_proc_pipe); - wr_proc_pipe = NULL; - } -} - -void -child_info_spawn::cleanup () -{ - if (moreinfo) - { - if (moreinfo->envp) - { - for (char **e = moreinfo->envp; *e; e++) - cfree (*e); - cfree (moreinfo->envp); - } - if (type != _CH_SPAWN && moreinfo->myself_pinfo) - CloseHandle (moreinfo->myself_pinfo); - cfree (moreinfo); - } - moreinfo = NULL; - if (ev) - { - CloseHandle (ev); - ev = NULL; - } - if (type == _CH_EXEC) - { - if (iscygwin () && hExeced) - proc_subproc (PROC_EXEC_CLEANUP, 0); - sync_proc_subproc.release (); - } - type = _CH_NADA; - child_info::cleanup (); -} - -/* Record any non-reaped subprocesses to be passed to about-to-be-execed - process. FIXME: There is a race here if the process exits while we - are recording it. */ -inline void -cygheap_exec_info::record_children () -{ - for (nchildren = 0; nchildren < nprocs; nchildren++) - { - children[nchildren].pid = procs[nchildren]->pid; - children[nchildren].p = procs[nchildren]; - } -} - -void -child_info_spawn::record_children () -{ - if (type == _CH_EXEC && iscygwin ()) - moreinfo->record_children (); -} - -/* Reattach non-reaped subprocesses passed in from the cygwin process - which previously operated under this pid. FIXME: Is there a race here - if the process exits during cygwin's exec handoff? */ -inline void -cygheap_exec_info::reattach_children (HANDLE parent) -{ - for (int i = 0; i < nchildren; i++) - { - pinfo p (parent, children[i].p, children[i].pid); - if (!p) - debug_only_printf ("couldn't reattach child %d from previous process", children[i].pid); - else if (!p.reattach ()) - debug_only_printf ("attach of child process %d failed", children[i].pid); - else - debug_only_printf ("reattached pid %d<%u>, process handle %p, rd_proc_pipe %p->%p", - p->pid, p->dwProcessId, p.hProcess, - children[i].p.rd_proc_pipe, p.rd_proc_pipe); - } -} - -void -child_info_spawn::reattach_children () -{ - moreinfo->reattach_children (parent); -} - -void -child_info::ready (bool execed) -{ - if (!subproc_ready) - { - sigproc_printf ("subproc_ready not set"); - return; - } - - if (dynamically_loaded) - sigproc_printf ("not really ready"); - else if (!SetEvent (subproc_ready)) - api_fatal ("SetEvent failed, %E"); - else - sigproc_printf ("signalled %p that I was ready", subproc_ready); - - if (execed) - { - CloseHandle (subproc_ready); - subproc_ready = NULL; - } -} - -bool -child_info::sync (pid_t pid, HANDLE& hProcess, DWORD howlong) -{ - bool res; - HANDLE w4[2]; - unsigned n = 0; - unsigned nsubproc_ready; - - if (!subproc_ready) - nsubproc_ready = WAIT_OBJECT_0 + 3; - else - { - w4[n++] = subproc_ready; - nsubproc_ready = 0; - } - w4[n++] = hProcess; - - sigproc_printf ("n %d, waiting for subproc_ready(%p) and child process(%p)", n, w4[0], w4[1]); - DWORD x = WaitForMultipleObjects (n, w4, FALSE, howlong); - x -= WAIT_OBJECT_0; - if (x >= n) - { - system_printf ("wait failed, pid %u, %E", pid); - res = false; - } - else - { - if (x != nsubproc_ready) - { - res = false; - GetExitCodeProcess (hProcess, &exit_code); - } - else - { - res = true; - exit_code = STILL_ACTIVE; - if (type == _CH_EXEC && my_wr_proc_pipe) - { - ForceCloseHandle1 (hProcess, childhProc); - hProcess = NULL; - } - } - sigproc_printf ("pid %u, WFMO returned %d, exit_code %y, res %d", pid, x, - exit_code, res); - } - return res; -} - -DWORD -child_info::proc_retry (HANDLE h) -{ - if (!exit_code) - return EXITCODE_OK; - sigproc_printf ("exit_code %y", exit_code); - switch (exit_code) - { - case STILL_ACTIVE: /* shouldn't happen */ - sigproc_printf ("STILL_ACTIVE? How'd we get here?"); - break; - case STATUS_DLL_NOT_FOUND: - case STATUS_ACCESS_VIOLATION: - case STATUS_ILLEGAL_INSTRUCTION: - case STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION: /* pseudo-reloc.c specific */ - return exit_code; - case STATUS_CONTROL_C_EXIT: - if (saw_ctrl_c ()) - return EXITCODE_OK; - /* fall through intentionally */ - case STATUS_DLL_INIT_FAILED: - case STATUS_DLL_INIT_FAILED_LOGOFF: - case EXITCODE_RETRY: - if (retry-- > 0) - exit_code = 0; - break; - case EXITCODE_FORK_FAILED: /* windows prevented us from forking */ - break; - - /* Count down non-recognized exit codes more quickly since they aren't - due to known conditions. */ - default: - if (!iscygwin () && (exit_code & 0xffff0000) != 0xc0000000) - break; - if ((retry -= 2) < 0) - retry = 0; - else - exit_code = 0; - } - if (!exit_code) - ForceCloseHandle1 (h, childhProc); - return exit_code; -} - -bool -child_info_fork::abort (const char *fmt, ...) -{ - if (fmt) - { - va_list ap; - va_start (ap, fmt); - strace_vprintf (SYSTEM, fmt, ap); - TerminateProcess (GetCurrentProcess (), EXITCODE_FORK_FAILED); - } - if (retry > 0) - TerminateProcess (GetCurrentProcess (), EXITCODE_RETRY); - return false; -} - -/* Check the state of all of our children to see if any are stopped or - * terminated. - */ -static int __reg1 -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; - } - - sigproc_printf ("no matching terminated children found"); - 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 bool __stdcall -remove_proc (int ci) -{ - if (have_execed) - { - if (_my_tls._ctinfo != procs[ci].wait_thread) - procs[ci].wait_thread->terminate_thread (); - } - else if (procs[ci]->exists ()) - return true; - - sigproc_printf ("removing procs[%d], pid %d, nprocs %d", ci, procs[ci]->pid, - nprocs); - if (procs[ci] != myself) - procs[ci].release (); - if (ci < --nprocs) - { - /* Wait for proc_waiter thread to make a copy of this element before - moving it or it may become confused. The chances are very high that - the proc_waiter thread has already done this by the time we - get here. */ - if (!have_execed && !exit_state) - while (!procs[nprocs].waiter_ready) - yield (); - procs[ci] = procs[nprocs]; - } - return 0; -} - -/* 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, pgid %d, w->pid %d", child->pid, child->pgid, w->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 false; - - int terminated; - - if (!((terminated = (child->process_state == PID_EXITED)) - || ((w->options & WCONTINUED) && child->stopsig == SIGCONT) - || ((w->options & WUNTRACED) && child->stopsig && child->stopsig != SIGCONT))) - return false; - - parent_w->next = w->next; /* successful wait. remove from wait queue */ - w->pid = child->pid; - - if (!terminated) - { - sigproc_printf ("stopped child, stop signal %d", child->stopsig); - if (child->stopsig == SIGCONT) - w->status = __W_CONTINUED; - else - w->status = (child->stopsig << 8) | 0x7f; - child->stopsig = 0; - } - else - { - child->process_state = PID_REAPED; - w->status = (__uint16_t) 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 (siginfo_t *si) -{ - unsigned size = sizeof (*si); - sigproc_printf ("pid %d wants some information", si->si_pid); - if (si->_si_commune._si_code & PICOM_EXTRASTR) - { - size_t n; - DWORD nb; - if (!ReadFile (my_readsig, &n, sizeof (n), &nb, NULL) || nb != sizeof (n)) - return; - siginfo_t *newsi = (siginfo_t *) alloca (size += n + 1); - *newsi = *si; - newsi->_si_commune._si_str = (char *) (newsi + 1); - if (!ReadFile (my_readsig, newsi->_si_commune._si_str, n, &nb, NULL) || nb != n) - return; - newsi->_si_commune._si_str[n] = '\0'; - si = newsi; - } - - pinfo pi (si->si_pid); - if (pi) - new cygthread (commune_process, size, si, "commune"); -} - -/* Add a packet to the beginning of the queue. - Should only be called from signal thread. */ -void -pending_signals::add (sigpacket& pack) -{ - sigpacket *se; - - se = sigs + pack.si.si_signo; - if (se->si.si_signo) - return; - *se = pack; - se->next = start.next; - start.next = se; -} - -/* Process signals by waiting for signal data to arrive in a pipe. - Set a completion event if one was specified. */ -static void WINAPI -wait_sig (VOID *) -{ - _sig_tls = &_my_tls; - bool sig_held = false; - - sigproc_printf ("entering ReadFile loop, my_readsig %p, my_sendsig %p", - my_readsig, my_sendsig); - - hntdll = GetModuleHandle ("ntdll.dll"); - - for (;;) - { - DWORD nb; - sigpacket pack = {}; - if (sigq.retry) - pack.si.si_signo = __SIGFLUSH; - else if (!ReadFile (my_readsig, &pack, sizeof (pack), &nb, NULL)) - Sleep (INFINITE); /* Assume were exiting. Never exit this thread */ - else if (nb != sizeof (pack) || !pack.si.si_signo) - { - system_printf ("garbled signal pipe data nb %u, sig %d", nb, pack.si.si_signo); - continue; - } - - sigq.retry = false; - /* Don't process signals when we start exiting */ - if (exit_state > ES_EXIT_STARTING && pack.si.si_signo > 0) - continue; - - sigset_t dummy_mask; - threadlist_t *tl_entry; - if (!pack.mask) - { - tl_entry = cygheap->find_tls (_main_tls); - dummy_mask = _main_tls->sigmask; - cygheap->unlock_tls (tl_entry); - pack.mask = &dummy_mask; - } - - sigpacket *q = &sigq.start; - bool clearwait = false; - switch (pack.si.si_signo) - { - case __SIGCOMMUNE: - talktome (&pack.si); - break; - case __SIGSTRACE: - strace.activate (false); - break; - case __SIGPENDING: - { - unsigned bit; - - *pack.mask = 0; - tl_entry = cygheap->find_tls (pack.sigtls); - while ((q = q->next)) - if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo))) - *pack.mask |= bit; - cygheap->unlock_tls (tl_entry); - } - break; - case __SIGHOLD: - sig_held = true; - break; - case __SIGSETPGRP: - init_console_handler (true); - break; - case __SIGTHREADEXIT: - { - /* Serialize thread exit as the thread exit code can be interpreted - as the process exit code in some cases when racing with - ExitProcess/TerminateProcess. - So, wait for the thread which sent this signal to exit, then - release the process lock which it held and close it's handle. - See cgf-000017 in DevNotes for more details. - */ - HANDLE h = (HANDLE) pack.si.si_cyg; - DWORD res = WaitForSingleObject (h, 5000); - lock_process::force_release (pack.sigtls); - ForceCloseHandle1 (h, exit_thread); - if (res != WAIT_OBJECT_0) - { -#ifdef DEBUGGING - try_to_debug(); -#endif - system_printf ("WaitForSingleObject(%p) for thread exit returned %u", h, res); - } - } - break; - default: /* Normal (positive) signal */ - if (pack.si.si_signo < 0) - sig_clear (-pack.si.si_signo); - else - sigq.add (pack); - case __SIGNOHOLD: - sig_held = false; - case __SIGFLUSH: - case __SIGFLUSHFAST: - if (!sig_held) - { - sigpacket *qnext; - /* Check the queue for signals. There will always be at least one - thing on the queue if this was a valid signal. */ - while ((qnext = q->next)) - { - if (qnext->si.si_signo && qnext->process () <= 0) - q = qnext; - else - { - q->next = qnext->next; - qnext->si.si_signo = 0; - } - } - if (pack.si.si_signo == SIGCHLD) - clearwait = true; - } - break; - } - if (clearwait && !have_execed) - proc_subproc (PROC_CLEARWAIT, 0); - if (pack.wakeup) - { - sigproc_printf ("signalling pack.wakeup %p", pack.wakeup); - SetEvent (pack.wakeup); - } - } -} |