diff options
-rw-r--r-- | winsup/cygwin/ChangeLog | 11 | ||||
-rw-r--r-- | winsup/cygwin/dcrt0.cc | 28 | ||||
-rw-r--r-- | winsup/cygwin/debug.cc | 225 | ||||
-rw-r--r-- | winsup/cygwin/sigproc.cc | 1379 | ||||
-rw-r--r-- | winsup/cygwin/winsup.h | 334 |
5 files changed, 1956 insertions, 21 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index f992778ccdd..dc103744e21 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,14 @@ +2003-09-22 Christopher Faylor <cgf@redhat.com> + + * dcrt0.cc (do_exit): Eliminate "C" linkage. Call events_terminate + early. + (exit_states): Move out of source file into header file. + * winsup.h: Move exit_states here. Remove "C" linkage from do_exit + declaration. + * debug.cc (lock_debug): Remove explicit (and incorrect) external for + exit_state. + * sigproc.cc (sig_dispatch_pending): Don't flush signals if exiting. + 2003-09-20 Christopher Faylor <cgf@redhat.com> * spawn.cc (pthread_cleanup): New struct. diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 51cc55f2529..1933f8cfe53 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -956,26 +956,18 @@ __main (void) do_global_ctors (user_data->ctors, FALSE); } -enum exit_states - { - ES_NOT_EXITING = 0, - ES_THREADTERM, - ES_SIGNAL, - ES_CLOSEALL, - ES_SIGPROCTERMINATE, - ES_TITLE, - ES_HUP_PGRP, - ES_HUP_SID, - ES_TTY_TERMINATE, - ES_EVENTS_TERMINATE - }; - exit_states NO_COPY exit_state; extern CRITICAL_SECTION exit_lock; -extern "C" void __stdcall +void __stdcall do_exit (int status) { + if (exit_state < ES_EVENTS_TERMINATE) + { + exit_state = ES_EVENTS_TERMINATE; + events_terminate (); + } + EnterCriticalSection (&exit_lock); UINT n = (UINT) status; @@ -1059,12 +1051,6 @@ do_exit (int status) tty_terminate (); } - if (exit_state < ES_EVENTS_TERMINATE) - { - exit_state = ES_EVENTS_TERMINATE; - events_terminate (); - } - minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n); myself->exit (n); } diff --git a/winsup/cygwin/debug.cc b/winsup/cygwin/debug.cc new file mode 100644 index 00000000000..1b4084febef --- /dev/null +++ b/winsup/cygwin/debug.cc @@ -0,0 +1,225 @@ +/* debug.cc + + Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +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 "sync.h" +#include "sigproc.h" +#include "pinfo.h" +#include "perthread.h" +#include "perprocess.h" +#include "security.h" +#include "cygerrno.h" +#ifdef DEBUGGING +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#endif + +#undef CloseHandle + +#ifdef DEBUGGING +/* Here lies extra debugging routines which help track down internal + Cygwin problems when compiled with -DDEBUGGING . */ +#include <stdlib.h> +#define NFREEH (sizeof (cygheap->debug.freeh) / sizeof (cygheap->debug.freeh[0])) + +class lock_debug +{ + static muto *locker; + bool acquired; + public: + lock_debug () : acquired (0) + { + if (locker && !exit_state) + acquired = !!locker->acquire (INFINITE); + } + void unlock () + { + if (locker && acquired) + { + locker->release (); + acquired = false; + } + } + ~lock_debug () {unlock ();} + friend void debug_init (); +}; + +muto NO_COPY *lock_debug::locker = NULL; + +static bool __stdcall mark_closed (const char *, int, HANDLE, const char *, BOOL); + +void +debug_init () +{ + muto *debug_lock_muto; + lock_debug::locker = new_muto (debug_lock_muto); +} + +/* Find a registered handle in the linked list of handles. */ +static handle_list * __stdcall +find_handle (HANDLE h) +{ + handle_list *hl; + for (hl = &cygheap->debug.starth; hl->next != NULL; hl = hl->next) + if (hl->next->h == h) + goto out; + cygheap->debug.endh = hl; + hl = NULL; + +out: + return hl; +} + +#ifdef DEBUGGING_AND_FDS_PROTECTED +void +setclexec (HANDLE oh, HANDLE nh, bool not_inheriting) +{ + handle_list *hl = find_handle (oh); + if (hl) + { + hl = hl->next; + hl->inherited = !not_inheriting; + hl->h = nh; + } +} +#endif + +/* Create a new handle record */ +static handle_list * __stdcall +newh () +{ + handle_list *hl; + lock_debug here; + + for (hl = cygheap->debug.freeh; hl < cygheap->debug.freeh + NFREEH; hl++) + if (hl->name == NULL) + return hl; + + return NULL; +} + +/* Add a handle to the linked list of known handles. */ +void __stdcall +add_handle (const char *func, int ln, HANDLE h, const char *name, bool inh) +{ + handle_list *hl; + lock_debug here; + + if ((hl = find_handle (h))) + { + hl = hl->next; + if (hl->name == name && hl->func == func && hl->ln == ln) + return; + system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func, + ln, name, h); + system_printf (" previously allocated by %s:%d(%s<%p>) winpid %d", + hl->func, hl->ln, hl->name, hl->h, hl->pid); + return; + } + + if ((hl = newh ()) == NULL) + { + here.unlock (); + debug_printf ("couldn't allocate memory for %s(%d): %s(%p)", + func, ln, name, h); + return; + } + hl->h = h; + hl->name = name; + hl->func = func; + hl->ln = ln; + hl->next = NULL; + hl->inherited = inh; + hl->pid = GetCurrentProcessId (); + cygheap->debug.endh->next = hl; + cygheap->debug.endh = hl; + debug_printf ("protecting handle '%s', inherited flag %d", hl->name, hl->inherited); + + return; +} + +static void __stdcall +delete_handle (handle_list *hl) +{ + handle_list *hnuke = hl->next; + debug_printf ("nuking handle '%s'", hnuke->name); + hl->next = hl->next->next; + memset (hnuke, 0, sizeof (*hnuke)); +} + +void +debug_fixup_after_fork_exec () +{ + /* No lock needed at this point */ + handle_list *hl; + for (hl = &cygheap->debug.starth; hl->next != NULL; /* nothing */) + if (hl->next->inherited) + hl = hl->next; + else + delete_handle (hl); // removes hl->next +} + +static bool __stdcall +mark_closed (const char *func, int ln, HANDLE h, const char *name, BOOL force) +{ + handle_list *hl; + lock_debug here; + + if ((hl = find_handle (h)) && !force) + { + hl = hl->next; + here.unlock (); // race here + system_printf ("attempt to close protected handle %s:%d(%s<%p>) winpid %d", + hl->func, hl->ln, hl->name, hl->h, hl->pid); + system_printf (" by %s:%d(%s<%p>)", func, ln, name, h); + return FALSE; + } + + handle_list *hln; + if (hl && (hln = hl->next) && strcmp (name, hln->name)) + { + system_printf ("closing protected handle %s:%d(%s<%p>)", + hln->func, hln->ln, hln->name, hln->h); + system_printf (" by %s:%d(%s<%p>)", func, ln, name, h); + } + + if (hl) + delete_handle (hl); + + return TRUE; +} + +/* Close a known handle. Complain if !force and closing a known handle or + if the name of the handle being closed does not match the registered name. */ +BOOL __stdcall +close_handle (const char *func, int ln, HANDLE h, const char *name, BOOL force) +{ + BOOL ret; + lock_debug here; + + if (!mark_closed (func, ln, h, name, force)) + return FALSE; + + ret = CloseHandle (h); + +#if 0 /* Uncomment to see CloseHandle failures */ + if (!ret) + small_printf ("CloseHandle(%s) failed %s:%d\n", name, func, ln); +#endif + return ret; +} + +int __stdcall +__set_errno (const char *func, int ln, int val) +{ + debug_printf ("%s:%d val %d", func, ln, val); + return errno = val; +} +#endif /*DEBUGGING*/ diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc new file mode 100644 index 00000000000..ed927ed3663 --- /dev/null +++ b/winsup/cygwin/sigproc.cc @@ -0,0 +1,1379 @@ +/* sigproc.cc: inter/intra signal and sub process handler + + Copyright 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + Written by Christopher Faylor <cgf@cygnus.com> + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <stdlib.h> +#include <time.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <assert.h> +#include <sys/signal.h> +#include "cygerrno.h" +#include "sync.h" +#include "sigproc.h" +#include "pinfo.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "child_info_magic.h" +#define NEED_VFORK +#include "perthread.h" +#include "shared_info.h" +#include "cygthread.h" + +/* + * Convenience defines + */ +#define WSSC 60000 // Wait for signal completion +#define WPSP 40000 // Wait for proc_subproc mutex +#define WSPX 20000 // Wait for wait_sig to terminate +#define WWSP 20000 // Wait for wait_subproc to terminate + +#define TOTSIGS (NSIG + __SIGOFFSET) + +#define wake_wait_subproc() SetEvent (events[0]) + +#define no_signals_available() (!hwait_sig || !sig_loop_wait) + +#define NZOMBIES 256 + +static LONG local_sigtodo[TOTSIGS]; +struct sigaction *global_sigs; + +inline LONG * +getlocal_sigtodo (int sig) +{ + return local_sigtodo + __SIGOFFSET + sig; +} + +void __stdcall +sigalloc () +{ + cygheap->sigs = global_sigs = + (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction)); +} + +void __stdcall +signal_fixup_after_exec () +{ + global_sigs = cygheap->sigs; + /* Set up child's signal handlers */ + for (int i = 0; i < NSIG; i++) + { + myself->getsig (i).sa_mask = 0; + if (myself->getsig (i).sa_handler != SIG_IGN) + myself->getsig (i).sa_handler = SIG_DFL; + } +} + +/* + * Global variables + */ +const char *__sp_fn ; +int __sp_ln; + +char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to + // current process but no wait is required +char NO_COPY myself_nowait_nonmain_dummy[1] = {'1'};// Flag to sig_send that signal goes to + // current process but no wait is required + // if this is not the main thread. + +HANDLE NO_COPY signal_arrived; // Event signaled when a signal has + // resulted in a user-specified + // function call +/* + * Common variables + */ + + +/* How long to wait for message/signals. Normally this is infinite. + * On termination, however, these are set to zero as a flag to exit. + */ + +#define Static static NO_COPY + +Static DWORD proc_loop_wait = 1000; // Wait for subprocesses to exit +Static DWORD sig_loop_wait = INFINITE; // Wait for signals to arrive + +Static HANDLE sigcatch_nonmain; // The semaphore signaled when + // signals are available for + // processing from non-main thread +Static HANDLE sigcatch_main; // Signalled when main thread sends a + // signal +Static HANDLE sigcatch_nosync; // Signal wait_sig to scan sigtodo + // but not to bother with any + // synchronization +Static HANDLE sigcomplete_main; // Event signaled when a signal has + // finished processing for the main + // thread +Static HANDLE sigcomplete_nonmain; // Semaphore raised for non-main + // threads when a signal has finished + // processing +HANDLE NO_COPY sigCONT; // Used to "STOP" a process +Static cygthread *hwait_sig; // Handle of wait_sig thread +Static cygthread *hwait_subproc; // Handle of sig_subproc thread + +Static HANDLE wait_sig_inited; // Control synchronization of + // message queue startup + +/* Used by WaitForMultipleObjects. These are handles to child processes. + */ +Static HANDLE events[PSIZE + 1]; // All my children's handles++ +#define hchildren (events + 1) // Where the children handles begin +Static char cpchildren[PSIZE * sizeof (pinfo)]; // All my children info +Static int nchildren; // Number of active children +Static char czombies[(NZOMBIES + 1) * sizeof (pinfo)]; // All my deceased children info +Static int nzombies; // Number of deceased children + +#define pchildren ((pinfo *) cpchildren) +#define zombies ((pinfo *) czombies) + +Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads +Static waitq waitq_main; // Storage for main thread + +muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff + +DWORD NO_COPY sigtid = 0; // ID of the signal thread + +bool NO_COPY pending_signals = false; // true if signals pending + +/* Functions + */ +static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1))); +static __inline__ BOOL get_proc_lock (DWORD, DWORD); +static HANDLE __stdcall getevent (_pinfo *, const char *) __attribute__ ((regparm (2))); +static void __stdcall remove_zombie (int); +static DWORD WINAPI wait_sig (VOID *arg); +static int __stdcall stopped_or_terminated (waitq *, _pinfo *); +static DWORD WINAPI wait_subproc (VOID *); + +/* Determine if the parent process is alive. + */ + +BOOL __stdcall +my_parent_is_alive () +{ + DWORD res; + if (!myself->ppid_handle) + { + debug_printf ("No myself->ppid_handle"); + res = FALSE; + } + else + for (int i = 0; i < 2; i++) + switch (res = WaitForSingleObject (myself->ppid_handle, 0)) + { + case WAIT_OBJECT_0: + debug_printf ("parent dead."); + res = FALSE; + goto out; + case WAIT_TIMEOUT: + debug_printf ("parent still alive"); + res = TRUE; + goto out; + case WAIT_FAILED: + DWORD werr = GetLastError (); + if (werr == ERROR_INVALID_HANDLE && i == 0) + continue; + system_printf ("WFSO for myself->ppid_handle(%p) failed, error %d", + myself->ppid_handle, werr); + res = FALSE; + goto out; + } +out: + return res; +} + +void __stdcall +wait_for_sigthread () +{ + sigproc_printf ("wait_sig_inited %p", wait_sig_inited); + HANDLE hsig_inited = wait_sig_inited; + (void) WaitForSingleObject (hsig_inited, INFINITE); + wait_sig_inited = NULL; + (void) ForceCloseHandle1 (hsig_inited, wait_sig_inited); +} + +/* Get the sync_proc_subproc muto to control access to + * children, zombie arrays. + * Attempt to handle case where process is exiting as we try to grab + * the mutex. + */ +static BOOL +get_proc_lock (DWORD what, DWORD val) +{ + Static int lastwhat = -1; + if (!sync_proc_subproc) + return FALSE; + if (sync_proc_subproc->acquire (WPSP)) + { + lastwhat = what; + return TRUE; + } + if (!sync_proc_subproc) + return FALSE; + system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), %E, last %d", + what, val, lastwhat); + return TRUE; +} + +static BOOL __stdcall +proc_can_be_signalled (_pinfo *p) +{ + if (p == myself_nowait || p == myself_nowait_nonmain || p == myself) + { + assert (!wait_sig_inited); + return 1; + } + + return ISSTATE (p, PID_INITIALIZING) || + (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) == + (PID_ACTIVE | PID_IN_USE)); +} + +BOOL __stdcall +pid_exists (pid_t pid) +{ + pinfo p (pid); + return proc_exists (p); +} + +/* Test to determine if a process really exists and is processing signals. + */ +BOOL __stdcall +proc_exists (_pinfo *p) +{ + return p && !(p->process_state & PID_EXITED); +} + +/* Return 1 if this is one of our children, zero otherwise. + FIXME: This really should be integrated with the rest of the proc_subproc + testing. Scanning these lists twice is inefficient. */ +int __stdcall +mychild (int pid) +{ + for (int i = 0; i < nchildren; i++) + if (pchildren[i]->pid == pid) + return 1; + for (int i = 0; i < nzombies; i++) + if (zombies[i]->pid == pid) + return 1; + return 0; +} + +/* Handle all subprocess requests + */ +#define vchild (*((pinfo *) val)) +int __stdcall +proc_subproc (DWORD what, DWORD val) +{ + int rc = 1; + int potential_match; + _pinfo *child; + int clearing; + waitq *w; + +#define wval ((waitq *) val) + + sigproc_printf ("args: %x, %d", what, val); + + if (!get_proc_lock (what, val)) // Serialize access to this function + { + system_printf ("couldn't get proc lock. Something is wrong."); + goto out1; + } + + switch (what) + { + /* Add a new subprocess to the children arrays. + * (usually called from the main thread) + */ + case PROC_ADDCHILD: + if (nchildren >= PSIZE - 1) + { + rc = 0; + break; + } + pchildren[nchildren] = vchild; + hchildren[nchildren] = vchild->hProcess; + if (!DuplicateHandle (hMainProc, vchild->hProcess, hMainProc, &vchild->pid_handle, + 0, 0, DUPLICATE_SAME_ACCESS)) + system_printf ("Couldn't duplicate child handle for pid %d, %E", vchild->pid); + ProtectHandle1 (vchild->pid_handle, pid_handle); + + if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid); + vchild->ppid = myself->pid; + vchild->uid = myself->uid; + vchild->gid = myself->gid; + vchild->pgid = myself->pgid; + vchild->sid = myself->sid; + vchild->ctty = myself->ctty; + vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + + sigproc_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p", + vchild->pid, nchildren, vchild->dwProcessId, + vchild->hProcess); + nchildren++; + + wake_wait_subproc (); + break; + + /* A child process had terminated. + Possibly this is just due to an exec(). Cygwin implements an exec() + as a "handoff" from one windows process to another. If child->hProcess + is different from what is recorded in hchildren, then this is an exec(). + Otherwise this is a normal child termination event. + (called from wait_subproc thread) */ + case PROC_CHILDTERMINATED: + if (hchildren[val] != pchildren[val]->hProcess) + { + sigproc_printf ("pid %d[%d], reparented old hProcess %p, new %p", + pchildren[val]->pid, val, hchildren[val], pchildren[val]->hProcess); + HANDLE h = hchildren[val]; + hchildren[val] = pchildren[val]->hProcess; /* Filled out by child */ + sync_proc_subproc->release (); // Release the lock ASAP + ForceCloseHandle1 (h, childhProc); + ProtectHandle1 (pchildren[val]->hProcess, childhProc); + rc = 0; + goto out; // This was an exec() + } + + sigproc_printf ("pid %d[%d] terminated, handle %p, nchildren %d, nzombies %d", + pchildren[val]->pid, val, hchildren[val], nchildren, nzombies); + + int thiszombie; + thiszombie = nzombies; + zombies[nzombies] = pchildren[val]; // Add to zombie array + zombies[nzombies++]->process_state = PID_ZOMBIE;// Walking dead + + sigproc_printf ("zombifying [%d], pid %d, handle %p, nchildren %d", + val, pchildren[val]->pid, hchildren[val], nchildren); + if ((int) val < --nchildren) + { + hchildren[val] = hchildren[nchildren]; + pchildren[val] = pchildren[nchildren]; + } + + /* See if we should care about the this terminated process. If we've + filled up our table or if we're ignoring SIGCHLD, then we immediately + remove the process and move on. Otherwise, this process becomes a zombie + which must be reaped by a wait() call. */ + if (nzombies >= NZOMBIES + || myself->getsig (SIGCHLD).sa_handler == (void *) SIG_IGN) + { + sigproc_printf ("automatically removing zombie %d", thiszombie); + remove_zombie (thiszombie); + } + + /* Don't scan the wait queue yet. Caller will send SIGCHLD to this process. + This will cause an eventual scan of waiters. */ + break; + + /* Handle a wait4() operation. Allocates an event for the calling + * thread which is signaled when the appropriate pid exits or stops. + * (usually called from the main thread) + */ + case PROC_WAIT: + wval->ev = NULL; // Don't know event flag yet + + if (wval->pid <= 0) + child = NULL; // Not looking for a specific pid + else if (!mychild (wval->pid)) + goto out; // invalid pid. flag no such child + + wval->status = 0; // Don't know status yet + sigproc_printf ("wval->pid %d, wval->options %d", wval->pid, wval->options); + + /* If the first time for this thread, create a new event, otherwise + * reset the event. + */ + if ((wval->ev = wval->thread_ev) == NULL) + { + wval->ev = wval->thread_ev = CreateEvent (&sec_none_nih, TRUE, + FALSE, NULL); + ProtectHandle (wval->ev); + } + + ResetEvent (wval->ev); + w = waitq_head.next; + waitq_head.next = wval; /* Add at the beginning. */ + wval->next = w; /* Link in rest of the list. */ + clearing = 0; + goto scan_wait; + + /* Clear all waiting threads. Called from exceptions.cc prior to + * the main thread's dispatch to a signal handler function. + * (called from wait_sig thread) + */ + case PROC_CLEARWAIT: + /* Clear all "wait"ing threads. */ + if (val) + sigproc_printf ("clear waiting threads"); + else + sigproc_printf ("looking for processes to reap"); + clearing = val; + + scan_wait: + /* Scan the linked list of wait()ing threads. If a wait's parameters + * match this pid, then activate it. + */ + for (w = &waitq_head; w->next != NULL; w = w->next) + { + if ((potential_match = checkstate (w)) > 0) + sigproc_printf ("released waiting thread"); + else if (!clearing && !(w->next->options & WNOHANG) && potential_match < 0) + sigproc_printf ("only found non-terminated children"); + else if (potential_match <= 0) // nothing matched + { + sigproc_printf ("waiting thread found no children"); + HANDLE oldw = w->next->ev; + w->next->pid = 0; + if (clearing) + w->next->status = -1; /* flag that a signal was received */ + else if (!potential_match || !(w->next->options & WNOHANG)) + w->next->ev = NULL; + if (!SetEvent (oldw)) + system_printf ("couldn't wake up wait event %p, %E", oldw); + w->next = w->next->next; + } + if (w->next == NULL) + break; + } + + if (!clearing) + sigproc_printf ("finished processing terminated/stopped child"); + else + { + waitq_head.next = NULL; + sigproc_printf ("finished clearing"); + } + break; + } + +out: + sync_proc_subproc->release (); // Release the lock +out1: + sigproc_printf ("returning %d", rc); + return rc; +} + +/* Terminate the wait_subproc thread. + * Called on process exit. + * Also called by spawn_guts to disassociate any subprocesses from this + * process. Subprocesses will then know to clean up after themselves and + * will not become zombies. + */ +void __stdcall +proc_terminate (void) +{ + sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies); + /* Signal processing is assumed to be blocked in this routine. */ + if (hwait_subproc) + { + proc_loop_wait = 0; // Tell wait_subproc thread to exit + sync_proc_subproc->acquire (WPSP); + wake_wait_subproc (); // Wake wait_subproc loop + hwait_subproc = NULL; + + (void) proc_subproc (PROC_CLEARWAIT, 1); + + /* Clean out zombie processes from the pid list. */ + int i; + for (i = 0; i < nzombies; i++) + { + if (zombies[i]->hProcess) + { + ForceCloseHandle1 (zombies[i]->hProcess, childhProc); + ForceCloseHandle1 (zombies[i]->pid_handle, pid_handle); + } + zombies[i]->ppid = 1; + zombies[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */ + zombies[i].release (); // FIXME: this breaks older gccs for some reason + } + + /* Disassociate my subprocesses */ + for (i = 0; i < nchildren; i++) + { + if (!pchildren[i]->hProcess) + sigproc_printf ("%d(%d) hProcess cleared already?", pchildren[i]->pid, + pchildren[i]->dwProcessId); + else + { + ForceCloseHandle1 (pchildren[i]->hProcess, childhProc); + sigproc_printf ("%d(%d) closed child handle", pchildren[i]->pid, + pchildren[i]->dwProcessId); + pchildren[i]->ppid = 1; + if (pchildren[i]->pgid == myself->pid) + pchildren[i]->process_state |= PID_ORPHANED; + } + pchildren[i].release (); + } + nchildren = nzombies = 0; + /* Just zero sync_proc_subproc as the delete below seems to cause + problems for older gccs. */ + sync_proc_subproc = NULL; + } + sigproc_printf ("leaving"); +} + +/* Clear pending signal from the sigtodo array + */ +void __stdcall +sig_clear (int sig) +{ + (void) InterlockedExchange (myself->getsigtodo (sig), 0L); + (void) InterlockedExchange (getlocal_sigtodo (sig), 0L); + return; +} + +extern "C" int +sigpending (sigset_t *set) +{ + unsigned bit; + *set = 0; + for (int sig = 1; sig < NSIG; sig++) + if ((*getlocal_sigtodo (sig) || *myself->getsigtodo (sig)) + && (myself->getsigmask () & (bit = SIGTOMASK (sig)))) + *set |= bit; + return 0; +} + +/* Force the wait_sig thread to wake up and scan the sigtodo array. + */ +extern "C" int __stdcall +sig_dispatch_pending () +{ + if (exit_state || !hwait_sig || GetCurrentThreadId () == sigtid) + return 0; + + sigframe thisframe (mainthread); + +#ifdef DEBUGGING + sigproc_printf ("pending_signals %d", pending_signals); +#endif + + if (!pending_signals) +#ifdef DEBUGGING + sigproc_printf ("no need to wake anything up"); +#else + ; +#endif + else + { + (void) sig_send (myself, __SIGFLUSH); +#ifdef DEBUGGING + sigproc_printf ("woke up wait_sig"); +#endif + } + + return thisframe.call_signal_handler (); +} + +/* Message initialization. Called from dll_crt0_1 + * + * This routine starts the signal handling thread. The wait_sig_inited + * event is used to signal that the thread is ready to handle signals. + * We don't wait for this during initialization but instead detect it + * in sig_send to gain a little concurrency. + */ +void __stdcall +sigproc_init () +{ + wait_sig_inited = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (wait_sig_inited); + + /* sync_proc_subproc is used by proc_subproc. It serialises + * access to the children and zombie arrays. + */ + new_muto (sync_proc_subproc); + + /* local event signaled when main thread has been dispatched + to a signal handler function. */ + signal_arrived = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (signal_arrived); + + hwait_sig = new cygthread (wait_sig, cygself, "sig"); + hwait_sig->zap_h (); + + /* Initialize waitq structure for main thread. A waitq structure is + * allocated for each thread that executes a wait to allow multiple threads + * to perform waits. Pre-allocate a waitq structure for the main thread. + */ + waitq *w; + if ((w = (waitq *)waitq_storage.get ()) == NULL) + { + w = &waitq_main; + waitq_storage.set (w); + } + memset (w, 0, sizeof *w); // Just to be safe + + myself->getsig (SIGSTOP).sa_flags = SA_RESTART | SA_NODEFER; + sigproc_printf ("process/signal handling enabled(%x)", myself->process_state); + return; +} + +/* Called on process termination to terminate signal and process threads. + */ +void __stdcall +sigproc_terminate (void) +{ + hwait_sig = NULL; + + if (!sig_loop_wait) + sigproc_printf ("sigproc handling not active"); + else + { + sigproc_printf ("entering"); + sig_loop_wait = 0; // Tell wait_sig to exit when it is + // finished with anything it is doing + ForceCloseHandle (sigcomplete_main); + for (int i = 0; i < 20; i++) + (void) ReleaseSemaphore (sigcomplete_nonmain, 1, NULL); + // ForceCloseHandle (sigcomplete_nonmain); + // ForceCloseHandle (sigcatch_main); + // ForceCloseHandle (sigcatch_nonmain); + // ForceCloseHandle (sigcatch_nosync); + } + proc_terminate (); // Terminate process handling thread + + return; +} + +/* Send a signal to another process by raising its signal semaphore. + * If pinfo *p == NULL, send to the current process. + * If sending to this process, wait for notification that a signal has + * completed before returning. + */ +int __stdcall +sig_send (_pinfo *p, int sig, DWORD ebp, bool exception) +{ + int rc = 1; + DWORD tid = GetCurrentThreadId (); + BOOL its_me; + HANDLE thiscatch = NULL; + HANDLE thiscomplete = NULL; + BOOL wait_for_completion; + sigframe thisframe; + + if (p == myself_nowait_nonmain) + p = (tid == mainthread.id) ? (_pinfo *) myself : myself_nowait; + if (!(its_me = (p == NULL || p == myself || p == myself_nowait))) + wait_for_completion = FALSE; + else + { + if (no_signals_available ()) + goto out; // Either exiting or not yet initializing + if (wait_sig_inited) + wait_for_sigthread (); + wait_for_completion = p != myself_nowait; + p = myself; + } + + /* It is possible that the process is not yet ready to receive messages + * or that it has exited. Detect this. + */ + if (!proc_can_be_signalled (p)) /* Is the process accepting messages? */ + { + sigproc_printf ("invalid pid %d(%x), signal %d", + p->pid, p->process_state, sig); + set_errno (ESRCH); + goto out; + } + + sigproc_printf ("pid %d, signal %d, its_me %d", p->pid, sig, its_me); + + LONG *todo; + bool issem; + if (its_me) + { + if (!wait_for_completion) + { + thiscatch = sigcatch_nosync; + todo = myself->getsigtodo (sig); + issem = false; + } + else if (tid != mainthread.id) + { + thiscatch = sigcatch_nonmain; + thiscomplete = sigcomplete_nonmain; + todo = getlocal_sigtodo (sig); + issem = true; + } + else + { + thiscatch = sigcatch_main; + thiscomplete = sigcomplete_main; + thisframe.init (mainthread, ebp, exception); + todo = getlocal_sigtodo (sig); + issem = true; + } + } + else if ((thiscatch = getevent (p, "sigcatch"))) + { + todo = p->getsigtodo (sig); + issem = false; + } + else + goto out; // Couldn't get the semaphore. getevent issued + // an error, if appropriate. + +#if WHEN_MULTI_THREAD_SIGNALS_WORK + signal_dispatch *sd; + sd = signal_dispatch_storage.get (); + if (sd == NULL) + sd = signal_dispatch_storage.create (); +#endif + + /* Increment the sigtodo array to signify which signal to assert. + */ + (void) InterlockedIncrement (todo); + + /* Notify the process that a signal has arrived. + */ + if (issem ? !ReleaseSemaphore (thiscatch, 1, NULL) : !SetEvent (thiscatch)) + { + /* Couldn't signal the semaphore. This probably means that the + * process is exiting. + */ + if (!its_me) + ForceCloseHandle (thiscatch); + else + { + if (no_signals_available ()) + sigproc_printf ("I'm going away now"); + else if ((int) GetLastError () == -1) + rc = WaitForSingleObject (thiscomplete, 500); + else + system_printf ("error sending signal %d to pid %d, semaphore %p, %E", + sig, p->pid, thiscatch); + } + goto out; + } + + /* No need to wait for signal completion unless this was a signal to + * this process. + * + * If it was a signal to this process, wait for a dispatched signal. + * Otherwise just wait for the wait_sig to signal that it has finished + * processing the signal. + */ + if (!wait_for_completion) + { + rc = WAIT_OBJECT_0; + sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d", its_me, sig); + if (!its_me) + ForceCloseHandle (thiscatch); + } + else + { + sigproc_printf ("Waiting for thiscomplete %p", thiscomplete); + rc = WaitForSingleObject (thiscomplete, WSSC); + } + + if (rc == WAIT_OBJECT_0) + rc = 0; // Successful exit + else + { + /* It's an error unless sig_loop_wait == 0 (the process is exiting). */ + if (!no_signals_available ()) + system_printf ("wait for sig_complete event failed, signal %d, rc %d, %E", + sig, rc); + set_errno (ENOSYS); + rc = -1; + } + +out: + sigproc_printf ("returning %d from sending signal %d", rc, sig); + return rc; +} + +/* Set pending signal from the sigtodo array + */ +void __stdcall +sig_set_pending (int sig) +{ + (void) InterlockedIncrement (getlocal_sigtodo (sig)); + return; +} + +/* Initialize the wait_subproc thread. + * Called from fork() or spawn() to initialize the handling of subprocesses. + */ +void __stdcall +subproc_init (void) +{ + if (hwait_subproc) + return; + + /* A "wakeup" handle which can be toggled to make wait_subproc reexamine + * the hchildren array. + */ + events[0] = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + hwait_subproc = new cygthread (wait_subproc, NULL, "proc"); + hwait_subproc->zap_h (); + ProtectHandle (events[0]); + sigproc_printf ("started wait_subproc thread"); +} + +/* Initialize some of the memory block passed to child processes + by fork/spawn/exec. */ + +void __stdcall +init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready) +{ + memset (ch, 0, sizeof *ch); + ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info); + ch->intro = PROC_MAGIC_GENERIC; + ch->magic = CHILD_INFO_MAGIC; + ch->type = chtype; + ch->cygpid = pid; + ch->subproc_ready = subproc_ready; + ch->pppid_handle = myself->ppid_handle; + ch->fhandler_union_cb = sizeof (fhandler_union); + ch->mount_h = cygwin_mount_h; +} + +/* Check the state of all of our children to see if any are stopped or + * terminated. + */ +static int __stdcall +checkstate (waitq *parent_w) +{ + int potential_match = 0; + + sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies); + + /* Check already dead processes first to see if they match the criteria + * given in w->next. + */ + for (int i = 0; i < nzombies; i++) + switch (stopped_or_terminated (parent_w, zombies[i])) + { + case -1: + potential_match = -1; + break; + case 1: + remove_zombie (i); + potential_match = 1; + goto out; + } + + sigproc_printf ("checking alive children"); + + /* No dead terminated children matched. Check for stopped children. */ + for (int i = 0; i < nchildren; i++) + switch (stopped_or_terminated (parent_w, pchildren[i])) + { + case -1: + potential_match = -1; + break; + case 1: + potential_match = 1; + goto out; + } + +out: + sigproc_printf ("returning %d", potential_match); + return potential_match; +} + +/* Get or create a process specific semaphore used in message passing. + */ +static HANDLE __stdcall +getevent (_pinfo *p, const char *str) +{ + HANDLE h; + char sem_name[MAX_PATH]; + + if (p != NULL) + { + if (!proc_can_be_signalled (p)) + { + set_errno (ESRCH); + return NULL; + } + int wait = 1000; + /* Wait for new process to generate its semaphores. */ + sigproc_printf ("pid %d, ppid %d, wait %d, initializing %x", p->pid, p->ppid, wait, + ISSTATE (p, PID_INITIALIZING)); + for (int i = 0; ISSTATE (p, PID_INITIALIZING) && i < wait; i++) + low_priority_sleep (1); + } + + if (p == NULL) + { + char sa_buf[1024]; + + DWORD winpid = GetCurrentProcessId (); +#if 0 + h = CreateSemaphore (sec_user_nih (sa_buf), init, max, + str = shared_name (sem_name, str, winpid)); +#else + h = CreateEvent (sec_user_nih (sa_buf), FALSE, FALSE, + str = shared_name (sem_name, str, winpid)); +#endif + p = myself; + if (!h) + { + system_printf ("can't create semaphore %s, %E", str); + __seterrno (); + } + } + else + { +#if 0 + h = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE, + shared_name (sem_name, str, p->dwProcessId)); +#else + h = OpenEvent (EVENT_ALL_ACCESS, FALSE, + shared_name (sem_name, str, p->dwProcessId)); +#endif + + if (!h) + { + if (GetLastError () == ERROR_FILE_NOT_FOUND && !proc_exists (p)) + set_errno (ESRCH); /* No such process */ + else + set_errno (EPERM); /* Couldn't access the semaphore -- + different cygwin DLL maybe? */ + } + } + + return h; +} + +/* Remove a zombie from zombies by swapping it with the last child in the list. + */ +static void __stdcall +remove_zombie (int ci) +{ + sigproc_printf ("removing %d, pid %d, nzombies %d", ci, zombies[ci]->pid, + nzombies); + + if (zombies[ci]) + { + ForceCloseHandle1 (zombies[ci]->hProcess, childhProc); + ForceCloseHandle1 (zombies[ci]->pid_handle, pid_handle); + zombies[ci].release (); + } + + if (ci < --nzombies) + zombies[ci] = zombies[nzombies]; + + return; +} + +/* Check status of child process vs. waitq member. + * + * parent_w is the pointer to the parent of the waitq member in question. + * child is the subprocess being considered. + * + * Returns + * 1 if stopped or terminated child matches parent_w->next criteria + * -1 if a non-stopped/terminated child matches parent_w->next criteria + * 0 if child does not match parent_w->next criteria + */ +static int __stdcall +stopped_or_terminated (waitq *parent_w, _pinfo *child) +{ + int potential_match; + waitq *w = parent_w->next; + + sigproc_printf ("considering pid %d", child->pid); + if (w->pid == -1) + potential_match = 1; + else if (w->pid == 0) + potential_match = child->pgid == myself->pgid; + else if (w->pid < 0) + potential_match = child->pgid == -w->pid; + else + potential_match = (w->pid == child->pid); + + if (!potential_match) + return 0; + + BOOL terminated; + + if ((terminated = child->process_state == PID_ZOMBIE) || + ((w->options & WUNTRACED) && child->stopsig)) + { + parent_w->next = w->next; /* successful wait. remove from wait queue */ + w->pid = child->pid; + + if (!terminated) + { + sigproc_printf ("stopped child"); + w->status = (child->stopsig << 8) | 0x7f; + child->stopsig = 0; + } + else /* Should only get here when child has been moved to the zombies array */ + { + DWORD status; + if (!GetExitCodeProcess (child->hProcess, &status)) + status = 0xffff; + if (status & EXIT_SIGNAL) + w->status = (status >> 8) & 0xff; /* exited due to signal */ + else + w->status = (status & 0xff) << 8; /* exited via "exit ()" */ + + add_rusage (&myself->rusage_children, &child->rusage_children); + add_rusage (&myself->rusage_children, &child->rusage_self); + + if (w->rusage) + { + add_rusage ((struct rusage *) w->rusage, &child->rusage_children); + add_rusage ((struct rusage *) w->rusage, &child->rusage_self); + } + } + + if (!SetEvent (w->ev)) /* wake up wait4 () immediately */ + system_printf ("couldn't wake up wait event %p, %E", w->ev); + return 1; + } + + return -potential_match; +} + +static void +talktome () +{ + winpids pids; + for (unsigned i = 0; i < pids.npids; i++) + if (pids[i]->hello_pid == myself->pid) + pids[i]->commune_recv (); +} + +#define RC_MAIN 0 +#define RC_NONMAIN 1 +#define RC_NOSYNC 2 +/* Process signals by waiting for a semaphore to become signaled. + * Then scan an in-memory array representing queued signals. + * Executes in a separate thread. + * + * Signals sent from this process are sent a completion signal so + * that returns from kill/raise do not occur until the signal has + * has been handled, as per POSIX. + */ +static DWORD WINAPI +wait_sig (VOID *self) +{ + LONG *todos[] = {getlocal_sigtodo (0), myself->getsigtodo (0)}; + /* Initialization */ + (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); + + /* sigcatch_nosync - semaphore incremented by sig_dispatch_pending and + * by foreign processes to force an examination of + * the sigtodo array. + * sigcatch_main - ditto for local main thread. + * sigcatch_nonmain - ditto for local non-main threads. + * + * sigcomplete_main - event used to signal main thread on signal + * completion + * sigcomplete_nonmain - semaphore signaled for non-main thread on signal + * completion + */ + sigcatch_nosync = getevent (NULL, "sigcatch"); + sigcatch_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); + sigcatch_main = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); + sigcomplete_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); + sigcomplete_main = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + sigproc_printf ("sigcatch_nonmain %p, sigcatch_main %p", sigcatch_nonmain, sigcatch_main); + sigCONT = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + + /* Setting dwProcessId flags that this process is now capable of receiving + * signals. Prior to this, dwProcessId was set to the windows pid of + * of the original windows process which spawned us unless this was a + * "toplevel" process. + */ + myself->dwProcessId = GetCurrentProcessId (); + myself->process_state |= PID_ACTIVE; + myself->process_state &= ~PID_INITIALIZING; + + ProtectHandle (sigcatch_nosync); + ProtectHandle (sigcatch_nonmain); + ProtectHandle (sigcatch_main); + ProtectHandle (sigcomplete_nonmain); + ProtectHandle (sigcomplete_main); + + /* If we've been execed, then there is still a stub left in the previous + * windows process waiting to see if it's started a cygwin process or not. + * Signalling subproc_ready indicates that we are a cygwin process. + */ + if (child_proc_info && child_proc_info->type == PROC_EXEC) + { + debug_printf ("subproc_ready %p", child_proc_info->subproc_ready); + if (!SetEvent (child_proc_info->subproc_ready)) + system_printf ("SetEvent (subproc_ready) failed, %E"); + ForceCloseHandle1 (child_proc_info->subproc_ready, subproc_ready); + /* Initialize an "indirect" pid block so that if someone looks up this + process via its Windows PID it will be redirected to the appropriate + Cygwin PID shared memory block. */ + static pinfo NO_COPY myself_identity; + myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED); + } + + SetEvent (wait_sig_inited); + sigtid = GetCurrentThreadId (); + + HANDLE catchem[] = {sigcatch_main, sigcatch_nonmain, sigcatch_nosync}; + sigproc_printf ("Ready. dwProcessid %d", myself->dwProcessId); + DWORD rc = RC_NOSYNC; + bool flush = false; + for (;;) + { + DWORD i; + if (rc == RC_MAIN || rc == RC_NONMAIN) + i = RC_NOSYNC; + else + i = RC_MAIN; + rc = WaitForSingleObject (catchem[i], 0); + if (rc != WAIT_OBJECT_0) + rc = WaitForMultipleObjects (3, catchem, FALSE, sig_loop_wait); + else + rc = i + WAIT_OBJECT_0; + (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); + + /* sigproc_terminate sets sig_loop_wait to zero to indicate that + this thread should terminate. */ + if (rc == WAIT_TIMEOUT) + { + if (!sig_loop_wait) + break; // Exiting + else + continue; + } + + if (rc == WAIT_FAILED) + { + if (sig_loop_wait != 0) + system_printf ("WFMO failed, %E"); + break; + } + + rc -= WAIT_OBJECT_0; + sigproc_printf ("awake, rc %d", rc); + LONG *todo; + if (rc != RC_NOSYNC) + todo = todos[0]; + else + todo = todos[1]; + + /* A sigcatch semaphore has been signaled. Scan the sigtodo + array looking for any unprocessed signals. */ + pending_signals = false; + unsigned more_signals = 0; + bool saw_failed_interrupt = false; + do + { + more_signals = 0; + for (int sig = -__SIGOFFSET; sig < NSIG; sig++) + { + LONG x = InterlockedDecrement (todo + sig); + if (x < 0) + InterlockedIncrement (todo + sig); + else if (x >= 0) + { + /* If x > 0, we have to deal with a signal at some later point */ + if (rc != RC_NOSYNC && x > 0) + /*pending_signals = true*/; // There should be an armed semaphore, in this case + + if (sig > 0 && sig != SIGKILL && sig != SIGSTOP && + (sigismember (&myself->getsigmask (), sig) || + main_vfork->pid || + (sig != SIGCONT && ISSTATE (myself, PID_STOPPED)))) + { + sigproc_printf ("signal %d blocked", sig); + x = InterlockedIncrement (myself->getsigtodo (sig)); + /* pending_signals = true;*/ // will be set by set_process_mask + } + else + { + sigproc_printf ("processing signal %d", sig); + switch (sig) + { + case __SIGFLUSH: + if (rc == RC_MAIN) + { + flush = true; + SetEvent (sigcatch_nosync); + goto out1; + } + break; + + /* Internal signal to turn on stracing. */ + case __SIGSTRACE: + strace.hello (); + break; + + case __SIGCOMMUNE: + talktome (); + break; + + /* A normal UNIX signal */ + default: + sigproc_printf ("Got signal %d", sig); + if (!sig_handle (sig)) + { + pending_signals = true; + saw_failed_interrupt = true; + x = InterlockedIncrement (myself->getsigtodo (sig)); + } + } + if (rc == RC_NOSYNC && x > 0) + more_signals++; + } + + if (sig == SIGCHLD) + proc_subproc (PROC_CLEARWAIT, 0); + + /* Need to take special action if an interrupt failed due to main thread not + getting around to calling handler yet. */ + if (saw_failed_interrupt || rc != RC_NOSYNC) + goto out; + } + } +#ifdef DEBUGGING + if (more_signals > 100) + system_printf ("hmm. infinite loop? more_signals %u\n", more_signals); +#endif + } + while (more_signals && sig_loop_wait); + + out: + /* Signal completion of signal handling depending on which semaphore + woke up the WaitForMultipleObjects above. */ + if (rc == RC_NONMAIN) // FIXME: This is broken + ReleaseSemaphore (sigcomplete_nonmain, 1, NULL); + else if (rc == RC_MAIN || flush) + { + SetEvent (sigcomplete_main); + sigproc_printf ("set main thread completion event"); + flush = false; + } + + out1: + if (saw_failed_interrupt) + { + SetEvent (sigcatch_nosync); + low_priority_sleep (0); /* Hopefully, other thread will be waking up soon. */ + } + sigproc_printf ("looping"); + } + + sigproc_printf ("done"); + ExitThread (0); +} + +/* Wait for subprocesses to terminate. Executes in a separate thread. */ +static DWORD WINAPI +wait_subproc (VOID *) +{ + sigproc_printf ("starting"); + int errloop = 0; + + for (;;) + { + DWORD rc = WaitForMultipleObjects (nchildren + 1, events, FALSE, + proc_loop_wait); + if (rc == WAIT_TIMEOUT) + if (!proc_loop_wait) + break; // Exiting + else + continue; + + if (rc == WAIT_FAILED) + { + if (!proc_loop_wait) + break; + + /* It's ok to get an ERROR_INVALID_HANDLE since another thread may have + closed a handle in the children[] array. So, we try looping a couple + of times to stabilize. FIXME - this is not foolproof. Probably, this + thread should be responsible for closing the children. */ + if (!errloop++) + proc_subproc (PROC_NOTHING, 0); // Just synchronize and continue + if (errloop < 10) + continue; + + system_printf ("wait failed. nchildren %d, wait %d, %E", + nchildren, proc_loop_wait); + + for (int i = 0; i <= nchildren; i++) + if ((rc = WaitForSingleObject (events[i], 0)) == WAIT_OBJECT_0 || + rc == WAIT_TIMEOUT) + continue; + else if (i == 0) + system_printf ("nchildren %d, event[%d] %p, %E", nchildren, i, events[i]); + else + { + system_printf ("nchildren %d, event[%d] %p, pchildren[%d] %p, events[0] %p, %E", + nchildren, i, events[i], i - 1, (_pinfo *) pchildren[i - 1], events[0]); + system_printf ("pid %d, dwProcessId %u, hProcess %p, progname '%s'", + pchildren[i - 1]->pid, pchildren[i - 1]->dwProcessId, + pchildren[i - 1]->hProcess, pchildren[i - 1]->progname); + } + break; + } + + errloop = 0; + rc -= WAIT_OBJECT_0; + if (rc-- != 0) + { + rc = proc_subproc (PROC_CHILDTERMINATED, rc); + if (!proc_loop_wait) // Don't bother if wait_subproc is + break; // exiting + + /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc + to avoid the proc_subproc lock since the signal thread will eventually + be calling proc_subproc and could unnecessarily block. */ + if (rc) + sig_send (myself_nowait, SIGCHLD); + } + sigproc_printf ("looping"); + } + + ForceCloseHandle (events[0]); + events[0] = NULL; + sigproc_printf ("done"); + ExitThread (0); +} + +extern "C" { +/* Provide a stack frame when calling WaitFor* functions */ + +#undef WaitForSingleObject + +DWORD __stdcall +WFSO (HANDLE hHandle, DWORD dwMilliseconds) +{ + DWORD ret; + sigframe thisframe (mainthread); + ret = WaitForSingleObject (hHandle, dwMilliseconds); + return ret; +} + +#undef WaitForMultipleObjects + +DWORD __stdcall +WFMO (DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds) +{ + DWORD ret; + sigframe thisframe (mainthread); + ret = WaitForMultipleObjects (nCount, lpHandles, fWaitAll, dwMilliseconds); + return ret; +} +} diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h new file mode 100644 index 00000000000..369f3ac8575 --- /dev/null +++ b/winsup/cygwin/winsup.h @@ -0,0 +1,334 @@ +/* winsup.h: main Cygwin header file. + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define __INSIDE_CYGWIN__ + +#define strlen __builtin_strlen +#define strcmp __builtin_strcmp +#define strcpy __builtin_strcpy +#define memcpy __builtin_memcpy +#define memcmp __builtin_memcmp +#ifdef HAVE_BUILTIN_MEMSET +# define memset __builtin_memset +#endif + +#define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy"))) +#define NO_COPY_INIT __attribute__((section(".data_cygwin_nocopy"))) + +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L +#define NEW_MACRO_VARARGS +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include <sys/types.h> +#include <sys/strace.h> + +/* Declarations for functions used in C and C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif +extern __uid32_t getuid32 (void); +extern __uid32_t geteuid32 (void); +extern int seteuid32 (__uid32_t); +extern __gid32_t getegid32 (void); +extern struct passwd *getpwuid32 (__uid32_t); +extern struct passwd *getpwnam (const char *); +extern struct __sFILE64 *fopen64 (const char *, const char *); +extern struct hostent *cygwin_gethostbyname (const char *name); +extern unsigned long cygwin_inet_addr (const char *cp); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +extern const char case_folded_lower[]; +#define cyg_tolower(c) (case_folded_lower[(unsigned char)(c)]) +extern const char case_folded_upper[]; +#define cyg_toupper(c) (case_folded_upper[(unsigned char)(c)]) + +#ifndef MALLOC_DEBUG +#define cfree newlib_cfree_dont_use +#endif + +#define WIN32_LEAN_AND_MEAN 1 +#define _WINGDI_H +#define _WINUSER_H +#define _WINNLS_H +#define _WINVER_H +#define _WINNETWK_H +#define _WINSVC_H +#include <windows.h> +#include <wincrypt.h> +#include <lmcons.h> +#undef _WINGDI_H +#undef _WINUSER_H +#undef _WINNLS_H +#undef _WINVER_H +#undef _WINNETWK_H +#undef _WINSVC_H + +#include "wincap.h" + +/* The one function we use from winuser.h most of the time */ +extern "C" DWORD WINAPI GetLastError (void); + +enum codepage_type {ansi_cp, oem_cp}; +extern codepage_type current_codepage; + +UINT get_cp (); + +int __stdcall sys_wcstombs(char *, const WCHAR *, int) + __attribute__ ((regparm(3))); + +int __stdcall sys_mbstowcs(WCHAR *, const char *, int) + __attribute__ ((regparm(3))); + +/* Used to check if Cygwin DLL is dynamically loaded. */ +extern int dynamically_loaded; + +extern int cygserver_running; + +#define _MT_SAFE // DELTEME someday + +#define TITLESIZE 1024 + +/* status bit manipulation */ +#define __ISSETF(what, x, prefix) \ + ((what)->status & prefix##_##x) +#define __SETF(what, x, prefix) \ + ((what)->status |= prefix##_##x) +#define __CLEARF(what, x, prefix) \ + ((what)->status &= ~prefix##_##x) +#define __CONDSETF(n, what, x, prefix) \ + ((n) ? __SETF (what, x, prefix) : __CLEARF (what, x, prefix)) + +#include "debug.h" + +/* Events/mutexes */ +extern HANDLE title_mutex; + +/**************************** Convenience ******************************/ + +/* Used when treating / and \ as equivalent. */ +#define isdirsep(ch) \ + ({ \ + char __c = (ch); \ + ((__c) == '/' || (__c) == '\\'); \ + }) + +/* Convert a signal to a signal mask */ +#define SIGTOMASK(sig) (1<<((sig) - signal_shift_subtract)) +extern unsigned int signal_shift_subtract; + +#ifdef NEW_MACRO_VARARGS +# define api_fatal(...) __api_fatal ("%P: *** " __VA_ARGS__) +#else +# define api_fatal(fmt, args...) __api_fatal ("%P: *** " fmt,## args) +#endif + +#undef issep +#define issep(ch) (strchr (" \t\n\r", (ch)) != NULL) + +#define isabspath(p) \ + (isdirsep (*(p)) || (isalpha (*(p)) && (p)[1] == ':' && (!(p)[2] || isdirsep ((p)[2])))) + +/******************** Initialization/Termination **********************/ + +class per_process; +/* cygwin .dll initialization */ +void dll_crt0 (per_process *) __asm__ ("_dll_crt0__FP11per_process"); +extern "C" void __stdcall _dll_crt0 (); + +/* dynamically loaded dll initialization */ +extern "C" int dll_dllcrt0 (HMODULE, per_process *); + +/* dynamically loaded dll initialization for non-cygwin apps */ +extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *); + +/* exit the program */ + +enum exit_states + { + ES_NOT_EXITING = 0, + ES_EVENTS_TERMINATE, + ES_THREADTERM, + ES_SIGNAL, + ES_CLOSEALL, + ES_SIGPROCTERMINATE, + ES_TITLE, + ES_HUP_PGRP, + ES_HUP_SID, + ES_TTY_TERMINATE + }; + +extern exit_states exit_state; +void __stdcall do_exit (int) __attribute__ ((regparm (1), noreturn)); + +/* UID/GID */ +void uinfo_init (void); + +#define ILLEGAL_UID16 ((__uid16_t)-1) +#define ILLEGAL_UID ((__uid32_t)-1) +#define ILLEGAL_GID16 ((__gid16_t)-1) +#define ILLEGAL_GID ((__gid32_t)-1) +#define ILLEGAL_SEEK ((_off64_t)-1) + +#define uid16touid32(u16) ((u16)==ILLEGAL_UID16?ILLEGAL_UID:(__uid32_t)(u16)) +#define gid16togid32(g16) ((g16)==ILLEGAL_GID16?ILLEGAL_GID:(__gid32_t)(g16)) + +/* various events */ +void events_init (void); +void events_terminate (void); + +void __stdcall close_all_files (void); + +/* Invisible window initialization/termination. */ +HWND __stdcall gethwnd (void); + +/* Globals that handle initialization of winsock in a child process. */ +extern HANDLE wsock32_handle; +extern HANDLE ws2_32_handle; + +/* Globals that handle initialization of netapi in a child process. */ +extern HANDLE netapi32_handle; + +/* debug_on_trap support. see exceptions.cc:try_to_debug() */ +extern "C" void error_start_init (const char*); +extern "C" int try_to_debug (bool waitloop = 1); + +void set_file_api_mode (codepage_type); + +extern int cygwin_finished_initializing; + +/**************************** Miscellaneous ******************************/ + +void __stdcall set_std_handle (int); +int __stdcall writable_directory (const char *file); +int __stdcall stat_dev (DWORD, int, unsigned long, struct __stat64 *); + +__ino64_t __stdcall hash_path_name (__ino64_t hash, const char *name) __attribute__ ((regparm(2))); +void __stdcall nofinalslash (const char *src, char *dst) __attribute__ ((regparm(2))); +extern "C" char *__stdcall rootdir (char *full_path) __attribute__ ((regparm(1))); + +/* String manipulation */ +extern "C" char *__stdcall strccpy (char *s1, const char **s2, char c); +extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribute__ ((regparm(2))); +extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3))); +extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2))); + +/* Time related */ +void __stdcall totimeval (struct timeval *dst, FILETIME * src, int sub, int flag); +long __stdcall to_time_t (FILETIME * ptr); +void __stdcall to_timestruc_t (FILETIME * ptr, timestruc_t * out); +void __stdcall time_as_timestruc_t (timestruc_t * out); + +void __stdcall set_console_title (char *); +void init_console_handler (); +void init_global_security (); + +int __stdcall check_null_str (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_empty_str (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_empty_str_errno (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_str_errno (const char *name) __attribute__ ((regparm(1))); +int __stdcall __check_null_invalid_struct (void *s, unsigned sz) __attribute__ ((regparm(2))); +int __stdcall __check_null_invalid_struct_errno (void *s, unsigned sz) __attribute__ ((regparm(2))); +int __stdcall __check_invalid_read_ptr_errno (const void *s, unsigned sz) __attribute__ ((regparm(2))); + +#define check_null_invalid_struct(s) \ + __check_null_invalid_struct ((s), sizeof (*(s))) +#define check_null_invalid_struct_errno(s) \ + __check_null_invalid_struct_errno ((s), sizeof (*(s))) + +struct iovec; +ssize_t check_iovec_for_read (const struct iovec *, int) __attribute__ ((regparm(2))); +ssize_t check_iovec_for_write (const struct iovec *, int) __attribute__ ((regparm(2))); + +#define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__) +void __set_winsock_errno (const char *fn, int ln) __attribute__ ((regparm(2))); + +extern bool wsock_started; + +/* Printf type functions */ +extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn)); +extern "C" int __small_sprintf (char *dst, const char *fmt, ...) /*__attribute__ ((regparm (2)))*/; +extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap) /*__attribute__ ((regparm (3)))*/; +extern void multiple_cygwin_problem (const char *, unsigned, unsigned); + +class path_conv; +int __stdcall stat_worker (const char *name, struct __stat64 *buf, int nofollow, + path_conv *pc = NULL) __attribute__ ((regparm (3))); +int __stdcall low_priority_sleep (DWORD) __attribute__ ((regparm (1))); +#define SLEEP_0_STAY_LOW INFINITE + +/**************************** Exports ******************************/ + +extern "C" { +int cygwin_select (int , fd_set *, fd_set *, fd_set *, + struct timeval *to); +int cygwin_gethostname (char *__name, size_t __len); + +int kill_pgrp (pid_t, int); +int _kill (int, int); +int _raise (int sig); + +extern DWORD binmode; +extern char _data_start__, _data_end__, _bss_start__, _bss_end__; +extern void (*__CTOR_LIST__) (void); +extern void (*__DTOR_LIST__) (void); +extern SYSTEM_INFO system_info; +}; + +/*************************** Unsorted ******************************/ + +#define WM_ASYNCIO 0x8000 // WM_APP + +/* Note that MAX_PATH is defined in the windows headers */ +/* There is also PATH_MAX and MAXPATHLEN. + PATH_MAX is from Posix and does *not* include the trailing NUL. + MAXPATHLEN is from Unix. + + Thou shalt use MAX_PATH throughout. It avoids the NUL vs no-NUL + issue and is neither of the Unixy ones [so we can punt on which + one is the right one to use]. */ + +#define STD_RBITS (S_IRUSR | S_IRGRP | S_IROTH) +#define STD_WBITS (S_IWUSR) +#define STD_XBITS (S_IXUSR | S_IXGRP | S_IXOTH) +#define NO_W ~(S_IWUSR | S_IWGRP | S_IWOTH) +#define NO_R ~(S_IRUSR | S_IRGRP | S_IROTH) +#define NO_X ~(S_IXUSR | S_IXGRP | S_IXOTH) + +/* The title on program start. */ +extern char *old_title; +extern bool display_title; + +extern HANDLE hMainThread; +extern HANDLE hMainProc; + +extern bool cygwin_testing; +extern unsigned _cygwin_testing_magic; +extern HMODULE cygwin_hmodule; + +extern char almost_null[]; + +#define winsock2_active (wsadata.wVersion >= 512) +#define winsock_active (wsadata.wVersion < 512) +extern struct WSAData wsadata; + +#endif /* defined __cplusplus */ |