/* Catch and send signals for Linux systems.
   Copyright 1994 Tristan Gingold
		  Written April 1994 by Tristan Gingold

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

The author may be reached by US/French mail:
		Tristan Gingold 
		8 rue Parmentier
		F-91120 PALAISEAU
		FRANCE
*/
#include "checker.h"
#include "errlist.h"

#define MAX_SIG_TO_SEND 4
/* This is computer, CPU, OS and perhaps OS-version dependant ! */

/* A list of signals to be delivered. */
struct sigtosend {
	unsigned int nsignal;
	unsigned int mask;
	void *func;
	struct sigcontext_struct context;
};

/* The function which resume. */
void _sig_go_on(void *);

/* The list. */
static struct sigtosend sigthread[MAX_SIG_TO_SEND];

/* The place to save the infos. */
static int thread_head = 0;

/* The signal to be send. */
static int thread_tail = 0;

/* Number of signals catched. */
int chkr_sig_catched = 0;

/* Save the signal arguments and resume.
 * NSIGNAL is the signal number
 * CONTEXT is the context.
 * FUNC is the function which will catch this signal.
 * MASK is the mask to set when the signal will be delivered. 
 */
void
_sig_save(int nsignal, STRUCT_SIG_CONTEXT *context, void *func, 
          unsigned int mask)
{
 struct sigtosend *tosend;
 
 if (chkr_sig_catched == MAX_SIG_TO_SEND)
   {
     chkr_perror(M_I_TMS_SG_ET);	/* Too many signals. */
     chkr_abort();
   }
 
 tosend = &sigthread[thread_head];
 thread_head = (thread_head + 1) % MAX_SIG_TO_SEND;
 
 /* Fill the fields. */
 tosend->nsignal = nsignal;
 tosend->mask = mask;
 tosend->func = func;
 memcpy(&tosend->context, context, sizeof(STRUCT_SIG_CONTEXT));
 
#if 0
 /* Just print a message */
 chkr_header("I received a signal (%d)\n", args[0]);
#endif

 /* Notify a signal has been catched */
 chkr_sig_catched++;
 
 /* Go on: resume as if no signals were catched. */
 _sig_go_on(context);
}

struct reg
{
  unsigned int eax;
  unsigned int ecx;
  unsigned int edx;
  unsigned int ebx;
  unsigned int esp;
  unsigned int ebp;
  unsigned int esi;
  unsigned int edi;
  unsigned int eip;
  unsigned char flags;
};

/* This function set the rights of the stack and call HANDLER.
 *  See sigjumpto.S. */
void
_send_sig_stub(void (*handler)(int, STRUCT_SIG_CONTEXT), int nsignal,
               int len, STRUCT_SIG_CONTEXT *sigcontext);

#if 0
/* Deliver signals. Called by codecheck.S: restore_register */
void _sig_send(struct reg *reg)
{
 struct sigtosend *tosend;
 struct sigcontext_struct sigcontext;

 do
   { 
     tosend = &sigthread[thread_tail];
     thread_tail = (thread_tail + 1) % MAX_SIG_TO_SEND;
 
     sigcontext = tosend->context;
     /* set sigcontext */
     sigcontext.eax = reg->eax;
     sigcontext.ecx = reg->ecx;
     sigcontext.edx = reg->edx;
     sigcontext.ebx = reg->ebx;
     sigcontext.esp = reg->esp;
     sigcontext.ebp = reg->ebp;
     sigcontext.esi = reg->esi;
     sigcontext.edi = reg->edi;
     sigcontext.eip = reg->eip;
     sigcontext.eflags = reg->flags;
     /* Don't forget this! */
     chkr_sig_catched--;
     /* set block mask */
     sigprocmask(SIG_SETMASK, &(tosend->mask), 0);
     /* And call */
     _send_sig_stub(tosend->func, tosend->nsignal, sizeof(STRUCT_SIG_CONTEXT),
               &sigcontext);
   }
 while (chkr_sig_catched);
   
 /* And return. */
 sigreturn (&sigcontext);
}
#endif

extern volatile int chkr_pending_signals;

/* Deliver signals. Called by codecheck.S: restore_register */
void _sig_send(struct reg *reg)
{
 struct reg saved_reg;
 sigset_t set;
 int n;
 
 saved_reg = *reg;
 
 if (!chkr_pending_signals)
   {
     chkr_printf("No signals to deliver in _sig_send\n");
     chkr_abort();
   }
   
 chkr_sig_catched = 0;
 
 while (chkr_pending_signals)
   {
     n = ffs(chkr_pending_signals);
     chkr_pending_signals &= ~n;
     __sigemptyset(&set);
     __sigaddset(&set, n);
     sigprocmask(SIG_UNBLOCK, &set, 0);
     if (kill(n, my_pid) != 0)
       {
         chkr_printf("Fail to send me a signal\n");
       }
   }

 *reg = saved_reg;
 return;    
