/* exceptions.cc

   Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.

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 <stdio.h>
#include <errno.h>

#define Win32_Winsock
#include "winsup.h"
#include "exceptions.h"
#undef DECLSPEC_IMPORT
#define DECLSPEC_IMPORT
#include <imagehlp.h>
#include "autoload.h"
#include "sync.h"

char debugger_command[2 * MAX_PATH + 20];

extern "C" {
static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
extern void sigreturn ();
extern void sigdelayed ();
extern void siglast ();
extern DWORD __sigfirst, __siglast;
};

static BOOL WINAPI ctrl_c_handler (DWORD);
static void really_exit (int);
static char windows_system_directory[1024];
static size_t windows_system_directory_length;

/* This is set to indicate that we have already exited.  */

static NO_COPY int exit_already = 0;
static NO_COPY muto *mask_sync = NULL;

HMODULE cygwin_hmodule;
HANDLE NO_COPY console_handler_thread_waiter = NULL;

static const struct
{
  unsigned int code;
  const char *name;
} status_info[] NO_COPY =
{
#define X(s) s, #s
  { X (STATUS_ABANDONED_WAIT_0) },
  { X (STATUS_ACCESS_VIOLATION) },
  { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
  { X (STATUS_BREAKPOINT) },
  { X (STATUS_CONTROL_C_EXIT) },
  { X (STATUS_DATATYPE_MISALIGNMENT) },
  { X (STATUS_FLOAT_DENORMAL_OPERAND) },
  { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
  { X (STATUS_FLOAT_INEXACT_RESULT) },
  { X (STATUS_FLOAT_INVALID_OPERATION) },
  { X (STATUS_FLOAT_OVERFLOW) },
  { X (STATUS_FLOAT_STACK_CHECK) },
  { X (STATUS_FLOAT_UNDERFLOW) },
  { X (STATUS_GUARD_PAGE_VIOLATION) },
  { X (STATUS_ILLEGAL_INSTRUCTION) },
  { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
  { X (STATUS_INTEGER_OVERFLOW) },
  { X (STATUS_INVALID_DISPOSITION) },
  { X (STATUS_IN_PAGE_ERROR) },
  { X (STATUS_NONCONTINUABLE_EXCEPTION) },
  { X (STATUS_NO_MEMORY) },
  { X (STATUS_PENDING) },
  { X (STATUS_PRIVILEGED_INSTRUCTION) },
  { X (STATUS_SINGLE_STEP) },
  { X (STATUS_STACK_OVERFLOW) },
  { X (STATUS_TIMEOUT) },
  { X (STATUS_USER_APC) },
  { X (STATUS_WAIT_0) },
  { 0, 0 }
#undef X
};

/* Initialization code.  */

#ifdef __i386__

// Set up the exception handler for the current thread.  The PowerPC & Mips
// use compiler generated tables to set up the exception handlers for each
// region of code, and the kernel walks the call list until it finds a region
// of code that handles exceptions.  The x86 on the other hand uses segment
// register fs, offset 0 to point to the current exception handler.

asm (".equ __except_list,0");

extern exception_list *_except_list asm ("%fs:__except_list");

static void
init_exception_handler (exception_list *el)
{
  el->handler = handle_exceptions;
  el->prev = _except_list;
  _except_list = el;
}

#define INIT_EXCEPTION_HANDLER(el) init_exception_handler (el)
#endif

void
set_console_handler ()
{
  /* Initialize global security attribute stuff */

  sec_none.nLength = sec_none_nih.nLength =
  sec_all.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
  sec_none.bInheritHandle = sec_all.bInheritHandle = TRUE;
  sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
  sec_none.lpSecurityDescriptor = sec_none_nih.lpSecurityDescriptor = NULL;
  sec_all.lpSecurityDescriptor = sec_all_nih.lpSecurityDescriptor =
    get_null_sd ();

  /* Allocate the event needed for ctrl_c_handler synchronization with
     wait_sig. */
  if (!console_handler_thread_waiter)
    CreateEvent (&sec_none_nih, TRUE, TRUE, NULL);
  (void) SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
  if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE))
    system_printf ("SetConsoleCtrlHandler failed, %E");
}

extern "C" void
init_exceptions (exception_list *el)
{
#ifdef INIT_EXCEPTION_HANDLER
  INIT_EXCEPTION_HANDLER (el);
#endif
}

extern "C" void
error_start_init (const char *buf)
{
  if (!buf || !*buf)
    {
      debugger_command[0] = '\0';
      return;
    }

  char myself_posix_name[MAX_PATH];

  /* FIXME: gdb cannot use win32 paths, but what if debugger isn't gdb? */
  cygwin_conv_to_posix_path (myself->progname, myself_posix_name);
  __small_sprintf (debugger_command, "%s %s", buf, myself_posix_name);
}

/* Utilities for dumping the stack, etc.  */

static void
exception (EXCEPTION_RECORD *e,  CONTEXT *in)
{
  const char *exception_name = 0;

  if (e)
    {
      for (int i = 0; status_info[i].name; i++)
	{
	  if (status_info[i].code == e->ExceptionCode)
	    {
	      exception_name = status_info[i].name;
	      break;
	    }
	}
    }

#ifdef __i386__
#define HAVE_STATUS
  if (exception_name)
    small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
  else
    small_printf ("Exception %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
  small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
	      in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
  small_printf ("ebp=%08x esp=%08x program=%s\r\n",
	      in->Ebp, in->Esp, myself->progname);
  small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
	      in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
#endif

#ifndef HAVE_STATUS
  system_printf ("Had an exception");
#endif
}

#ifdef __i386__
/* Print a stack backtrace. */

#define HAVE_STACK_TRACE

/* The function used to load the imagehlp DLL.  Returns TRUE if the
   DLL was found. */
static LoadDLLinitfunc (imagehlp)
{
  imagehlp_handle = LoadLibrary ("imagehlp.dll");
  return !!imagehlp_handle;
}

LoadDLLinit (imagehlp)	/* Set up storage for imagehlp.dll autoload */
LoadDLLfunc (StackWalk, StackWalk@36, imagehlp)

/* A class for manipulating the stack. */
class stack_info
{
  int first_time;		/* True if just starting to iterate. */
  int walk ();		 	/* Uses the "old" method */
public:
  STACKFRAME sf;		/* For storing the stack information */
  void init (DWORD);		/* Called the first time that stack info is needed */
  stack_info (): first_time(1) {}

  /* Postfix ++ iterates over the stack, returning zero when nothing is left. */
  int operator ++(int) { return this->walk (); }
};

/* The number of parameters used in STACKFRAME */
#define NPARAMS (sizeof(thestack.sf.Params) / sizeof(thestack.sf.Params[0]))

/* This is the main stack frame info for this process. */
static NO_COPY stack_info thestack;
signal_dispatch NO_COPY sigsave;

/* Initialize everything needed to start iterating. */
void
stack_info::init (DWORD ebp)
{
  first_time = 1;
  memset (&sf, 0, sizeof(sf));
  sf.AddrFrame.Offset = ebp;
  sf.AddrPC.Offset = ((DWORD *) ebp)[1];
  sf.AddrFrame.Mode = AddrModeFlat;
}

/* Walk the stack by looking at successive stored 'bp' frames.
   This is not foolproof. */
int
stack_info::walk ()
{
  char **ebp;
  if (first_time)
    /* Everything is filled out already */
    ebp = (char **) sf.AddrFrame.Offset;
  else if ((ebp = (char **) *(char **) sf.AddrFrame.Offset) != NULL)
    {
      sf.AddrFrame.Offset = (DWORD) ebp;
      sf.AddrPC.Offset = sf.AddrReturn.Offset;
    }
  else
    return 0;

  first_time = 0;
  if (!sf.AddrPC.Offset)
    return 0;		/* stack frames are exhausted */

  /* The return address always follows the stack pointer */
  sf.AddrReturn.Offset = (DWORD) *++ebp;

  /* The arguments follow the return address */
  for (unsigned i = 0; i < NPARAMS; i++)
    sf.Params[i] = (DWORD) *++ebp;
  return 1;
}

/* Dump the stack using either the old method or the new Win32 API method */
void
stack (HANDLE hproc, HANDLE hthread, CONTEXT *cx)
{
  int i;

  thestack.init (cx->Ebp);	/* Initialize from the input CONTEXT */
  small_printf ("Stack trace:\r\nFrame     Function  Args\r\n");
  for (i = 0; i < 16 && thestack++ ; i++)
    {
      small_printf ("%08x  %08x ", thestack.sf.AddrFrame.Offset,
		    thestack.sf.AddrPC.Offset);
      for (unsigned j = 0; j < NPARAMS; j++)
	small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
      small_printf (")\r\n");
    }
  small_printf ("End of stack trace%s",
	      i == 16 ? " (more stack frames may be present)" : "");
}

/* Temporary (?) function for external callers to get a stack dump */
extern "C" void
cygwin_stackdump()
{
  CONTEXT c;
  c.ContextFlags = CONTEXT_FULL;
  HANDLE h1 = GetCurrentProcess ();
  HANDLE h2 = GetCurrentThread ();
  GetThreadContext (h2, &c);
  stack(h1, h2, &c);
}

static int NO_COPY keep_looping = 0;

extern "C" int
try_to_debug ()
{
  debug_printf ("debugger_command %s", debugger_command);
  if (*debugger_command == '\0')
    return 0;

  __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());

  BOOL dbg;

  PROCESS_INFORMATION pi = {NULL, 0, 0, 0};

  STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
  si.lpReserved = NULL;
  si.lpDesktop = NULL;
  si.dwFlags = 0;
  si.cb = sizeof (si);

  /* FIXME: need to know handles of all running threads to
     suspend_all_threads_except (current_thread_id);
  */

  /* if any of these mutexes is owned, we will fail to start any cygwin app
     until trapped app exits */

  ReleaseMutex (pinfo_mutex);
  ReleaseMutex (title_mutex);

  dbg = CreateProcess (NULL,
		       debugger_command,
		       NULL,
		       NULL,
		       FALSE,
		       CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
		       NULL,
		       NULL,
		       &si,
		       &pi);
  if (!dbg)
    {
      system_printf ("Failed to start debugger: %E");
      /* FIXME: need to know handles of all running threads to
        resume_all_threads_except (current_thread_id);
      */
    }
  else
    {
      keep_looping = 1;
      while (keep_looping)
	Sleep (10000);
    }

  return 0;
}

void
stackdump (HANDLE hproc, HANDLE hthread, EXCEPTION_RECORD *e, CONTEXT *in)
{
  char *p;
  if (myself->progname[0])
    {
      /* write to progname.stackdump if possible */
      if ((p = strrchr (myself->progname, '\\')))
	p++;
      else
	p = myself->progname;
      char corefile[strlen(p) + sizeof(".stackdump")];
      __small_sprintf (corefile, "%s.stackdump", p);
      HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
			     CREATE_ALWAYS, 0, 0);
      if (h != INVALID_HANDLE_VALUE)
	{
	  system_printf ("Dumping stack trace to %s", corefile);
	  SetStdHandle (STD_ERROR_HANDLE, h);
	}
    }
  if (e)
    exception (e, in);
  stack (hproc, hthread, in);
}

/* Main exception handler. */

static int
handle_exceptions (EXCEPTION_RECORD *e, void *, CONTEXT *in, void *)
{
  int sig;

  /* If we've already exited, don't do anything here.  Returning 1
     tells Windows to keep looking for an exception handler.  */
  if (exit_already)
    return 1;

  /* Coerce win32 value to posix value.  */
  switch (e->ExceptionCode)
    {
    case STATUS_FLOAT_DENORMAL_OPERAND:
    case STATUS_FLOAT_DIVIDE_BY_ZERO:
    case STATUS_FLOAT_INEXACT_RESULT:
    case STATUS_FLOAT_INVALID_OPERATION:
    case STATUS_FLOAT_OVERFLOW:
    case STATUS_FLOAT_STACK_CHECK:
    case STATUS_FLOAT_UNDERFLOW:
    case STATUS_INTEGER_DIVIDE_BY_ZERO:
    case STATUS_INTEGER_OVERFLOW:
      sig = SIGFPE;
      break;

    case STATUS_ILLEGAL_INSTRUCTION:
    case STATUS_PRIVILEGED_INSTRUCTION:
    case STATUS_NONCONTINUABLE_EXCEPTION:
      sig = SIGILL;
      break;

    case STATUS_TIMEOUT:
      sig = SIGALRM;
      break;

    case STATUS_ACCESS_VIOLATION:
    case STATUS_DATATYPE_MISALIGNMENT:
    case STATUS_ARRAY_BOUNDS_EXCEEDED:
    case STATUS_GUARD_PAGE_VIOLATION:
    case STATUS_IN_PAGE_ERROR:
    case STATUS_NO_MEMORY:
    case STATUS_INVALID_DISPOSITION:
    case STATUS_STACK_OVERFLOW:
      sig = SIGSEGV;
      break;

    case STATUS_CONTROL_C_EXIT:
      sig = SIGINT;
      break;

    case STATUS_INVALID_HANDLE:
      /* CloseHandle will throw this exception if it is given an
	 invalid handle.  We don't care about the exception; we just
	 want CloseHandle to return an error.  This can be revisited
	 if gcc ever supports Windows style structured exception
	 handling.  */
      return 0;

    default:
      /* If we don't recognize the exception, we have to assume that
	 we are doing structured exception handling, and we let
	 something else handle it.  */
      return 1;
    }

  debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
  debug_printf ("In cygwin_except_handler sig = %d at %p", sig, in->Eip);

  if (myself->getsig(sig).sa_mask & SIGTOMASK (sig))
    syscall_printf ("signal %d, masked %p", sig, myself->getsig(sig).sa_mask);

  if (!myself->progname[0]
      || (void *) myself->getsig(sig).sa_handler == (void *) SIG_DFL
      || (void *) myself->getsig(sig).sa_handler == (void *) SIG_IGN
      || (void *) myself->getsig(sig).sa_handler == (void *) SIG_ERR)
    {
      static NO_COPY int traced = 0;

      /* Print the exception to the console */
      if (e)
	{
	  for (int i = 0; status_info[i].name; i++)
	    {
	      if (status_info[i].code == e->ExceptionCode)
		{
		  system_printf ("Exception: %s", status_info[i].name);
		  break;
		}
	    }
	}

      /* Another exception could happen while tracing or while exiting.
	 Only do this once.  */
      if (traced++)
        system_printf ("Error while dumping state (probably corrupted stack)");
      else
	{
	  HANDLE hthread;
	  DuplicateHandle (hMainProc, GetCurrentThread (),
			   hMainProc, &hthread, 0, FALSE, DUPLICATE_SAME_ACCESS);
	  stackdump (hMainProc, hthread, e, in);
	}
      try_to_debug ();
      really_exit (EXIT_SIGNAL | sig);
    }

  debug_printf ("In cygwin_except_handler calling %p",
		 myself->getsig(sig).sa_handler);

  DWORD *ebp = (DWORD *)in->Esp;
  for (DWORD *bpend = ebp - 8; ebp > bpend; ebp--)
    if (*ebp == in->SegCs && ebp[-1] == in->Eip)
      {
	ebp -= 2;
	break;
      }

  sig_send (NULL, sig, (DWORD) ebp);		// Signal myself
  return 0;
}
#endif /* __i386__ */

#ifndef HAVE_STACK_TRACE
void
stack (void)
{
  system_printf ("Stack trace not yet supported on this machine.");
}
#endif

/* Utilities to call a user supplied exception handler.  */

#define SIG_NONMASKABLE	(SIGTOMASK (SIGCONT) | SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))

#ifdef __i386__
#define HAVE_CALL_HANDLER

/* Non-raceable sigsuspend
 * Note: This implementation is based on the Single UNIX Specification
 * man page.  This indicates that sigsuspend always returns -1 and that
 * attempts to block unblockable signals will be silently ignored.
 * This is counter to what appears to be documented in some UNIX
 * man pages, e.g. Linux.
 */
int __stdcall
handle_sigsuspend (sigset_t tempmask)
{
  sigset_t oldmask = myself->getsigmask ();	// Remember for restoration

  set_process_mask (tempmask & ~SIG_NONMASKABLE);// Let signals we're
				//  interested in through.
  sigproc_printf ("old mask %x, new mask %x", oldmask, tempmask);

  WaitForSingleObject (signal_arrived, INFINITE);

  set_sig_errno (EINTR);	// Per POSIX

  /* A signal dispatch function will have been added to our stack and will
     be hit eventually.  Set the old mask to be restored when the signal
     handler returns. */

  sigsave.oldmask = oldmask;	// Will be restored by signal handler
  return -1;
}

extern DWORD exec_exit;		// Possible exit value for exec
extern int pending_signals;

int
interruptible (DWORD pc)
{
#if 0
  DWORD pchigh = pc & 0xf0000000;
  return ((pc >= (DWORD) &__sigfirst) && (pc <= (DWORD) &__siglast)) ||
	 !(pchigh == 0xb0000000 || pchigh == 0x70000000 || pchigh == 0x60000000);
#else
  int res = 1;
  if ((pc >= (DWORD) &__sigfirst) && (pc <= (DWORD) &__siglast))
    res = 0;
  else
    {
      MEMORY_BASIC_INFORMATION m;
      memset (&m, 0, sizeof m);
      if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m))
	sigproc_printf ("couldn't get memory info, %E");

      char *checkdir = (char *) alloca (windows_system_directory_length);
#     define h ((HMODULE) m.AllocationBase)
      if (h == cygwin_hmodule)
	res = 0;
      else if (!GetModuleFileName (h, checkdir, windows_system_directory_length))
	res = 0;
      else
        res = !strncasematch (windows_system_directory, checkdir,
			      windows_system_directory_length);
    }

  sigproc_printf ("interruptible %d", res);
  return res;
# undef h
#endif
}

static void __stdcall
interrupt_setup (int sig, struct sigaction& siga, void *handler, DWORD retaddr)
{
  sigsave.retaddr = retaddr;
  sigsave.oldmask = myself->getsigmask ();	// Remember for restoration
  /* FIXME: Not multi-thread aware */
  set_process_mask (myself->getsigmask () | siga.sa_mask | SIGTOMASK (sig));
  sigsave.func = (void (*)(int)) handler;
  sigsave.sig = sig;
  sigsave.saved_errno = -1;		// Flag: no errno to save
  sigsave.ebp = 0;
}

static void
interrupt_now (CONTEXT *ctx, int sig, struct sigaction& siga, void *handler)
{
  interrupt_setup (sig, siga, handler, ctx->Eip);
  ctx->Eip = (DWORD) sigdelayed;
  SetThreadContext (myself->getthread2signal(), ctx); /* Restart the thread */
}

static int
interrupt_on_return (DWORD ebp, int sig, struct sigaction& siga, void *handler)
{
  int i;

  if (sigsave.sig)
    return 0;	/* Already have a signal stacked up */

  thestack.init (ebp);  /* Initialize from the input CONTEXT */
  for (i = 0; i < 32 && thestack++ ; i++)
    if (interruptible (thestack.sf.AddrReturn.Offset))
      {
	DWORD *addr_retaddr = ((DWORD *)thestack.sf.AddrFrame.Offset) + 1;
	if (*addr_retaddr  == thestack.sf.AddrReturn.Offset)
	  {
	    interrupt_setup (sig, siga, handler, *addr_retaddr);
	    *addr_retaddr = (DWORD) sigdelayed;
	  }
	break;
      }

  return 1;
}

extern "C" void __stdcall
set_sig_errno (int e)
{
  set_errno (e);
  sigsave.saved_errno = e;
  debug_printf ("errno %d", e);
}

static int
call_handler (int sig, struct sigaction& siga, void *handler, int nonmain)
{
  CONTEXT cx;
  DWORD ebp;
  int interrupted = 1;
  HANDLE hth = NULL;
  int res;

  if (!nonmain)
    ebp = sigsave.ebp;
  else
    {
      hth = myself->getthread2signal ();
      /* Suspend the thread which will receive the signal.  But first ensure that
	 this thread doesn't have the sync_proc_subproc and mask_sync mutos, since
	 we need those (hack alert).  If the thread-to-be-suspended has either of
	 these mutos, enter a busy loop until it is released.  If the thread is
	 already suspended (which should never occur) then just queue the signal. */
      for (;;)
	{
	  sigproc_printf ("suspending mainthread");
	  res = SuspendThread (hth);

	  muto *m;
	  /* FIXME: Make multi-thread aware */
	  for (m = muto_start.next;  m != NULL; m = m->next)
	    if (m->unstable () || m->owner () == maintid)
	      goto keep_looping;

	  break;

	keep_looping:
	  sigproc_printf ("suspended thread owns a muto (%s)", m->name);
	  if (res)
	      goto set_pending;

	  ResumeThread (hth);
	  Sleep (0);
	}

      sigproc_printf ("SuspendThread returned %d", res);

      cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
      if (!GetThreadContext (hth, &cx))
	{
	  system_printf ("couldn't get context of main thread, %E");
	  goto out;
	}
      ebp = cx.Ebp;
    }

  if (hExeced != NULL || (nonmain && interruptible (cx.Eip)))
    interrupt_now (&cx, sig, siga, handler);
  else if (!interrupt_on_return (ebp, sig, siga, handler))
    {
    set_pending:
      pending_signals = 1;	/* FIXME: Probably need to be more tricky here */
      sig_set_pending (sig);
      interrupted = 0;
    }

  if (interrupted)
    {
      res = SetEvent (signal_arrived);	// For an EINTR case
      sigproc_printf ("armed signal_arrived %p, res %d", signal_arrived, res);
      /* Clear any waiting threads prior to dispatching to handler function */
      proc_subproc(PROC_CLEARWAIT, 1);
    }

out:
  if (!hth)
    sigproc_printf ("modified main-thread stack");
  else
    {
      res = ResumeThread (hth);
      sigproc_printf ("ResumeThread returned %d", res);
    }

  sigproc_printf ("returning %d", interrupted);
  return interrupted;
}
#endif /* i386 */

#ifndef HAVE_CALL_HANDLER
#error "Need to supply machine dependent call_handler"
#endif

/* Keyboard interrupt handler.  */
static BOOL WINAPI
ctrl_c_handler (DWORD type)
{
  if (type == CTRL_LOGOFF_EVENT)
    return TRUE;

  /* Wait for sigproc_init to tell us that it's safe to send something.
     This event will always be in a signalled state when wait_sig is
     ready to process signals. */
  (void) WaitForSingleObject (console_handler_thread_waiter, 5000);

  if ((type == CTRL_CLOSE_EVENT) || (type == CTRL_SHUTDOWN_EVENT))
    /* Return FALSE to prevent an "End task" dialog box from appearing
       for each Cygwin process window that's open when the computer
       is shut down or console window is closed. */
    {
      sig_send (NULL, SIGHUP);
      return FALSE;
    }
  tty_min *t = cygwin_shared->tty.get_tty(myself->ctty);
  /* Ignore this if we're not the process group lead since it should be handled
     *by* the process group leader. */
  if (t->getpgid () != myself->pid ||
      (GetTickCount () - t->last_ctrl_c) < MIN_CTRL_C_SLOP)
    return TRUE;
  else
    /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
       that we have handled the signal).  At this point, type should be
       a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
    {
      t->last_ctrl_c = GetTickCount ();
      kill (-myself->pid, SIGINT);
      t->last_ctrl_c = GetTickCount ();
      return TRUE;
    }
}

/* Set the signal mask for this process.
 * Note that some signals are unmaskable, as in UNIX.
 */
extern "C" void __stdcall
set_process_mask (sigset_t newmask)
{
  extern DWORD sigtid;

  mask_sync->acquire (INFINITE);
  sigset_t oldmask = myself->getsigmask ();
  newmask &= ~SIG_NONMASKABLE;
  sigproc_printf ("old mask = %x, new mask = %x", myself->getsigmask (), newmask);
  myself->setsigmask (newmask);	// Set a new mask
  mask_sync->release ();
  if (oldmask != newmask && GetCurrentThreadId () != sigtid)
    sig_dispatch_pending ();
  return;
}

extern "C" {
static void
sig_handle_tty_stop (int sig)
{
#if 0
  HANDLE waitbuf[2];

  /* Be sure that process's main thread isn't an owner of vital
     mutex to prevent cygwin subsystem lockups */
  waitbuf[0] = pinfo_mutex;
  waitbuf[1] = title_mutex;
  WaitForMultipleObjects (2, waitbuf, TRUE, INFINITE);
  ReleaseMutex (pinfo_mutex);
  ReleaseMutex (title_mutex);
#endif
  myself->stopsig = sig;
  myself->process_state |= PID_STOPPED;
  /* See if we have a living parent.  If so, send it a special signal.
   * It will figure out exactly which pid has stopped by scanning
   * its list of subprocesses.
   */
  if (my_parent_is_alive ())
    {
      pinfo *parent = procinfo(myself->ppid);
      sig_send (parent, __SIGCHILDSTOPPED);
    }
  sigproc_printf ("process %d stopped by signal %d, parent_alive %p",
		  myself->pid, sig, parent_alive);
  /* There is a small race here with the above two mutexes */
  SuspendThread (hMainThread);
  return;
}
}

int __stdcall
sig_handle (int sig, int nonmain)
{
  int rc = 0;

  sigproc_printf ("signal %d", sig);

  struct sigaction thissig = myself->getsig(sig);
  void *handler = (void *) thissig.sa_handler;

  myself->rusage_self.ru_nsignals++;

  /* Clear pending SIGCONT on stop signals */
  if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
    sig_clear (SIGCONT);

  if (sig == SIGKILL)
    goto exit_sig;

  if (sig == SIGSTOP)
    goto stop;

  /* FIXME: Should we still do this if SIGCONT has a handler? */
  if (sig == SIGCONT)
    {
      myself->stopsig = 0;
      myself->process_state &= ~PID_STOPPED;
      /* Clear pending stop signals */
      sig_clear (SIGSTOP);
      sig_clear (SIGTSTP);
      sig_clear (SIGTTIN);
      sig_clear (SIGTTOU);
      /* Windows 95 hangs on resuming non-suspended thread */
      SuspendThread (hMainThread);
      while (ResumeThread (hMainThread) > 1)
	;
      /* process pending signals */
      sig_dispatch_pending ();
    }

#if 0
  char sigmsg[24];
  __small_sprintf (sigmsg, "cygwin: signal %d\n", sig);
  OutputDebugString (sigmsg);
#endif

  if (handler == (void *) SIG_DFL)
    {
      if (sig == SIGCHLD || sig == SIGIO || sig == SIGCONT || sig == SIGWINCH)
	{
	  sigproc_printf ("default signal %d ignored", sig);
	  goto done;
	}

      if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
	goto stop;

      goto exit_sig;
    }

  if (handler == (void *) SIG_IGN)
    {
      sigproc_printf ("signal %d ignored", sig);
      goto done;
    }

  if (handler == (void *) SIG_ERR)
    goto exit_sig;

  if ((sig == SIGCHLD) && (thissig.sa_flags & SA_NOCLDSTOP))
    goto done;

  goto dosig;

stop:
  handler = (void *) sig_handle_tty_stop;

dosig:
  /* Dispatch to the appropriate function. */
  sigproc_printf ("signal %d, about to call %p", sig, thissig.sa_handler);
  rc = call_handler (sig, thissig, handler, nonmain);

done:
  sigproc_printf ("returning %d", rc);
  return rc;

exit_sig:
  if (sig == SIGQUIT || sig == SIGABRT)
    {
      stackdump (NULL, NULL, NULL, NULL);
      try_to_debug ();
    }
  sigproc_printf ("signal %d, about to call do_exit", sig);
  TerminateThread (hMainThread, 0);
  /* FIXME: This just works around the problem so that we don't attempt to
     use a resource lock when exiting.  */
  user_data->resourcelocks->Delete();
  user_data->resourcelocks->Init();
  do_exit (EXIT_SIGNAL | (sig << 8));
  /* Never returns */
}

/* Cover function to `do_exit' to handle exiting even in presence of more
   exceptions.  We use to call exit, but a SIGSEGV shouldn't cause atexit
   routines to run.  */

static void
really_exit (int rc)
{
  /* If the exception handler gets a trap, we could recurse awhile.
     If this is non-zero, skip the cleaning up and exit NOW.  */

  if (exit_already++)
    {
      /* We are going down - reset our process_state without locking. */
      myself->record_death (FALSE);
      ExitProcess (rc);
    }

  do_exit (rc);
}

HANDLE NO_COPY pinfo_mutex = NULL;
HANDLE NO_COPY title_mutex = NULL;

void
events_init (void)
{
  /* pinfo_mutex protects access to process table */

  if (!(pinfo_mutex = CreateMutex (&sec_all_nih, FALSE,
				   shared_name ("pinfo_mutex", 0))))
    api_fatal ("catastrophic failure - unable to create pinfo_mutex, %E");

  ProtectHandle (pinfo_mutex);

  /* title_mutex protects modification of console title. It's neccessary
     while finding console window handle */

  if (!(title_mutex = CreateMutex (&sec_all_nih, FALSE,
				   shared_name ("title_mutex", 0))))
    api_fatal ("can't create title mutex, %E");

  ProtectHandle (title_mutex);
  mask_sync = new_muto (FALSE, "mask_sync");
  windows_system_directory[0] = '\0';
  (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
  char *end = strchr (windows_system_directory, '\0');
  if (end == windows_system_directory)
    api_fatal ("can't find windows system directory");
  if (end[-1] != '\\')
    {
      *end++ = '\\';
      *end = '\0';
    }
  windows_system_directory_length = end - windows_system_directory;
  debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
		windows_system_directory, windows_system_directory_length);
  debug_printf ("cygwin_hmodule %p", cygwin_hmodule);
}

void
events_terminate (void)
{
//CloseHandle (pinfo_mutex);	// Use implicit close on exit to avoid race
  ForceCloseHandle (title_mutex);
  exit_already = 1;
}

#define pid_offset (unsigned)(((pinfo *)NULL)->pid)
extern "C" {
static void __stdcall
reset_signal_arrived () __attribute__ ((unused));
static void __stdcall
reset_signal_arrived ()
{
  (void) ResetEvent (signal_arrived);
  sigproc_printf ("reset signal_arrived");
}

void unused_sig_wrapper()
{
/* Signal cleanup stuff.  Cleans up stack (too bad that we didn't
   prototype signal handlers as __stdcall), calls _set_process_mask
   to restore any mask, restores any potentially clobbered registered
   and returns to orignal caller. */
__asm__ volatile ("
	.text
	.globl	__raise
__raise:
	pushl	%%ebp
	movl	%%esp,%%ebp
	movl	8(%%ebp),%%eax
	pushl	%%eax
	movl	$_myself,%%eax
	pushl	%6(%%eax)
	call	__kill
	mov	%%ebp,%%esp
	popl	%%ebp
	ret

_sigreturn:
	addl	$4,%%esp
	call	_set_process_mask@4
	popl	%%eax		# saved errno
	testl	%%eax,%%eax	# Is it < 0
	jl	1f		# yup.  ignore it
	movl	%1,%%ebx
	movl	%%eax,(%%ebx)
1:	popl	%%eax
	popl	%%ebx
	popl	%%ecx
	popl	%%edx
	popl	%%edi
	popl	%%esi
	popf
	ret

_sigdelayed:
	pushl	%2	# original return address
	pushf
	pushl	%%esi
	pushl	%%edi
	pushl	%%edx
	pushl	%%ecx
	pushl	%%ebx
	pushl	%%eax
	pushl	%7	# saved errno
	pushl	%3	# oldmask
	pushl	%4	# signal argument
	pushl	$_sigreturn

	call	_reset_signal_arrived@0
#	pushl	_signal_arrived	# Everybody waiting for this should
#	call	_ResetEvent@4	# have woken up by now.
	movl	$0,%0

	cmpl	$0,_pending_signals
	je	2f
___sigfirst:
	pushl	$0
	call	_sig_dispatch_pending@4
___siglast:

2:	jmp	*%5

" : "=m" (sigsave.sig) : "m" (&_impure_ptr->_errno),
  "g" (sigsave.retaddr), "g" (sigsave.oldmask), "g" (sigsave.sig),
    "g" (sigsave.func), "o" (pid_offset), "g" (sigsave.saved_errno)
  );
}
}