8cb359d947
* dcrt0.cc (dll_crt0_0): Remove hexec_proc stuff. * fork.cc (fork_child): Remove call to pinfo_fixup_after_fork. * pinfo.cc (set_myself): Close and zero pid_handle if set. (pinfo_fixup_after_fork): Delete. (proc_waiter): Don't close vchild.hProcess here. Do that when we are remove the vchild from procs. Save hProcess as pid_handle only on first reparent operation. (pinfo::wait): Don't set pid_handle here. (pinfo::alert_parent): Always try to send signal. If unsuccessful then close and zero wr_proc_pipe. * pinfo.h (pinfo::pinfo): Make sure that appropriate parts of the class are zeroed on construction. (pinfo::alert_parent): Take char argument. (pinfo_fixup_after_fork): Delete declaration. (hexec_proc): Ditto. * sigproc.cc (remove_proc): Close pid_handle and hProcess if appropriate. * spawn.cc (spawn_guts): Set cygheap->pid_handle on first exec. * cygheap.h (init_cygheap::pid_handle): New element. * pinfo.cc (set_myself): Clear previously existing cygheap->pid_handle when a new process has been started. (pinfo::wait): Make sure that a handle to the newly forked/spawned process is kept around so that the pid will not be reused. * pinfo.h (_pinfo::pid_handle): Move. (pinfo::pid_handle): to here. * spawn.cc (spawn_guts): Create a pid_handle in cygheap prior to spawning to ensure that the pid does not get reused during the lifetime of the "cygwin pid". * pinfo.h (pinfo::alert_parent): New function. * exceptions.cc (sig_handle_tty_stop): Use alert_parent to send "signals" to parent. * fork.cc (fork_parent): Don't close pi.hProcess. Let the waiter thread do that. * pinfo.cc (proc_waiter): Detect case where process exits without setting the exit code and use value from GetExitCodeProcess. Reluctantly implement __SIGREPARENT. (pinfo::alert_parent): Define. * sigproc.h (__SIGREPARENT): New enum. * spawn.cc (spawn_guts): Send reparent signal to parent on exec. Always create process in suspended state to avoid races. Remove cygthread.h in favor of cygtls.h throughout since cygtls now includes cygthread.h. Eliminate ppid_handle usage throughout. * child_info.h: Regenerate magic number (child_info): Remove pppid_handle. * cygthread.h (cygthread::release): New method. Frees thread without waiting. * cygthread.cc (cygthread::stub): Set _ctinfo in _mytls to point to information for executing thread. Don't call SetEvent if thread is no longer in use. (cygthread::simplestub): Ditto. * cygtls.h (_cygtls::_ctinfo): New element contains pointer to information about executing cygthread, if any. * dcrt0.cc: Remove last vestiges of per_thread stuff. (dll_crt0_0): Ditto. Remove accommodation for ppid_handle. (do_exit): Remove obsolete reparenting test. (_exit): Exit with a more SUSv3-like exit value. * dtable.cc (dtable::stdio_init): Check for myself->cygstarted rather than myself->ppid_handle to see if we were started by a cygwin process. * exceptions.cc (open_stackdumpfile): Ditto. (handle_exceptions): Ditto. (ctrl_c_handler): Ditto. (sig_handle_tty_stop): Ditto. Let parent send signal to itself on STOP. (sigpacket::process): Comment out vfork test. (signal_exit): Use more SUSv3-like exit value on signal. * external.cc (fillout_pinfo): Don't set hProcess. * fork.cc: Remove VFORK cruft. (per_thread::set): Delete. (fork_child): Remove perthread stuff. (fork_parent): Remove obsolete subproc_init. Accommodate new method for tracking subprocesses. * pinfo.cc (set_myself): Accommodate new pinfo/_pinfo layout. Set some things here that used to be set in wait_sig. (_pinfo::exit): Set exitcode here. Close process pipe. (_pinfo::commune_send): Accommodeate new pinfo/_pinfo layout. (proc_waiter): New function. Waits, in a thread for subprocess to go away. (pinfo::wait): New function. Initialization for proc_waiter. * pinfo.h (_pinfo::exitcode): New element. (_pinfo::cygstarted): Ditto. (_pinfo::wr_proc_pipe): Ditto. (_pinfo::ppid_handle): Delete. (_pinfo::hProcess): Delete. (_pinfo::lock): Delete. (pinfo::hProcess): New element. (pinfo::lock): Ditto. (pinfo::wait): Declare new function. (pinfo::preserve): Define new function. * sigproc.cc: Remove old stuff from wait_subproc thread based method. (zombies): Remove. (procs): New. (my_parent_is_alive): Just check that the parent pid exists. (mychild): Just use pinfo methods to determine if child is mine. (proc_subproc): Revamp PROC_ADDCHILD to use pinfo::wait. Remove PROC_CHILDTERMINATED logic. Use different method to remove processes from list when SIGCHLD == SIG_IGN. (proc_terminate): Gut. (subproc_init): Delete. (init_child_info): Remove setting of pppid_handle. (checkstate): Revamp to only scan procs array. (remove_proc): Rename from remove_zombie. Don't close hProcess or pid_handle. Don't release memory if it's myself. (stopped_or_terminated): Change logic to handle new consolidated proc/zombie array. (wait_subproc): Delete. * sigproc.h: Remove obsolete EXIT_* defines. (subproc_init): Remove declaration. * spawn.cc (spawn_guts): Remove reparenting stuff. Use standard wait logic to wait for child if started from a non-cygwin process. * tlsoffsets.h: Regenerate. * tty.cc (tty_init): Check for myself->cygstarted rather than myself->ppid_handle to see if we were started by a cygwin process. * include/sys/signal.h (external_pinfo::exitcode): Replace hProcess. * include/sys/wait.h (WCOREDUMP): Define. * fhandler_tty.cc (fhandler_tty_slave::read): Add debugging output for timeout case. * signal.cc (abort): Flag that we are exiting with the ABORT signal.
1032 lines
26 KiB
C++
1032 lines
26 KiB
C++
/* 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 "cygtls.h"
|
|
#include "sigproc.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 bool __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");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 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 (); // clean up process stuff
|
|
|
|
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;
|
|
}
|
|
|
|
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 (proc_exists (procs[ci]))
|
|
return true;
|
|
|
|
sigproc_printf ("removing procs[%d], pid %d, nprocs %d", ci, procs[ci]->pid,
|
|
nprocs);
|
|
if (procs[ci] != myself)
|
|
{
|
|
procs[ci].release ();
|
|
if (procs[ci].pid_handle)
|
|
ForceCloseHandle1 (procs[ci].pid_handle, childhProc);
|
|
if (procs[ci].hProcess)
|
|
ForceCloseHandle1 (procs[ci].hProcess, childhProc);
|
|
}
|
|
if (ci < --nprocs)
|
|
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", 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->process_state |= PID_ACTIVE;
|
|
myself->process_state &= ~PID_INITIALIZING;
|
|
|
|
sigproc_printf ("myself->dwProcessId %u", myself->dwProcessId);
|
|
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);
|
|
}
|