/* go-signal.c -- signal handling for Go.

   Copyright 2009 The Go Authors. All rights reserved.
   Use of this source code is governed by a BSD-style
   license that can be found in the LICENSE file.  */

#include <signal.h>
#include <stdlib.h>

#include "go-assert.h"
#include "go-panic.h"
#include "go-signal.h"

#include "runtime.h"

#undef int 

#ifndef SA_ONSTACK
#define SA_ONSTACK 0
#endif

/* What to do for a signal.  */

struct sigtab
{
  /* Signal number.  */
  int sig;
  /* Nonzero if the signal should be ignored.  */
  _Bool ignore;
};

/* What to do for signals.  */

static struct sigtab signals[] =
{
  { SIGHUP, 0 },
  { SIGINT, 0 },
  { SIGALRM, 1 },
  { SIGTERM, 0 },
#ifdef SIGBUS
  { SIGBUS, 0 },
#endif
#ifdef SIGFPE
  { SIGFPE, 0 },
#endif
#ifdef SIGUSR1
  { SIGUSR1, 1 },
#endif
#ifdef SIGSEGV
  { SIGSEGV, 0 },
#endif
#ifdef SIGUSR2
  { SIGUSR2, 1 },
#endif
#ifdef SIGPIPE
  { SIGPIPE, 1 },
#endif
#ifdef SIGCHLD
  { SIGCHLD, 1 },
#endif
#ifdef SIGTSTP
  { SIGTSTP, 1 },
#endif
#ifdef SIGTTIN
  { SIGTTIN, 1 },
#endif
#ifdef SIGTTOU
  { SIGTTOU, 1 },
#endif
#ifdef SIGURG
  { SIGURG, 1 },
#endif
#ifdef SIGXCPU
  { SIGXCPU, 1 },
#endif
#ifdef SIGXFSZ
  { SIGXFSZ, 1 },
#endif
#ifdef SIGVTARLM
  { SIGVTALRM, 1 },
#endif
#ifdef SIGPROF
  { SIGPROF, 1 },
#endif
#ifdef SIGWINCH
  { SIGWINCH, 1 },
#endif
#ifdef SIGIO
  { SIGIO, 1 },
#endif
#ifdef SIGPWR
  { SIGPWR, 1 },
#endif
  { -1, 0 }
};

/* The Go signal handler.  */

static void
sighandler (int sig)
{
  const char *msg;
  int i;

  /* FIXME: Should check siginfo for more information when
     available.  */
  msg = NULL;
  switch (sig)
    {
#ifdef SIGBUS
    case SIGBUS:
      msg = "invalid memory address or nil pointer dereference";
      break;
#endif

#ifdef SIGFPE
    case SIGFPE:
      msg = "integer divide by zero or floating point error";
      break;
#endif

#ifdef SIGSEGV
    case SIGSEGV:
      msg = "invalid memory address or nil pointer dereference";
      break;
#endif

    default:
      break;
    }

  if (msg != NULL)
    {
      sigset_t clear;

      if (__sync_bool_compare_and_swap (&m->mallocing, 1, 1))
	{
	  fprintf (stderr, "caught signal while mallocing: %s\n", msg);
	  __go_assert (0);
	}

      /* The signal handler blocked signals; unblock them.  */
      i = sigfillset (&clear);
      __go_assert (i == 0);
      i = sigprocmask (SIG_UNBLOCK, &clear, NULL);
      __go_assert (i == 0);

      __go_panic_msg (msg);
    }

  if (__go_sigsend (sig))
    return;
  for (i = 0; signals[i].sig != -1; ++i)
    {
      if (signals[i].sig == sig)
	{
	  struct sigaction sa;

	  if (signals[i].ignore)
	    return;

	  memset (&sa, 0, sizeof sa);

	  sa.sa_handler = SIG_DFL;

	  i = sigemptyset (&sa.sa_mask);
	  __go_assert (i == 0);

	  if (sigaction (sig, &sa, NULL) != 0)
	    abort ();

	  raise (sig);
	  exit (2);
	}
    }
  abort ();
}

/* Initialize signal handling for Go.  This is called when the program
   starts.  */

void
__initsig ()
{
  struct sigaction sa;
  int i;

  siginit ();

  memset (&sa, 0, sizeof sa);

  sa.sa_handler = sighandler;

  i = sigfillset (&sa.sa_mask);
  __go_assert (i == 0);

  for (i = 0; signals[i].sig != -1; ++i)
    if (sigaction (signals[i].sig, &sa, NULL) != 0)
      __go_assert (0);
}