/*
 * Wine signal handling
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <setjmp.h>

#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/wait.h>

#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#ifdef HAVE_SYSCALL_H
# include <syscall.h>
#else
# ifdef HAVE_SYS_SYSCALL_H
#  include <sys/syscall.h>
# endif
#endif

#include "miscemu.h"
#include "selectors.h"
#include "sig_context.h"
#include "winsock.h"

/* Linux sigaction function */

#if defined(linux) && defined(__i386__)
/* This is the sigaction structure from the Linux 2.1.20 kernel.  */
#undef sa_handler
struct kernel_sigaction
{
    void (*sa_handler)();
    unsigned long sa_mask;
    unsigned long sa_flags;
    void (*sa_restorer)();
};

/* Similar to the sigaction function in libc, except it leaves alone the
   restorer field, which is used to specify the signal stack address */
static __inline__ int wine_sigaction( int sig, struct kernel_sigaction *new,
                                      struct kernel_sigaction *old )
{
#ifdef __PIC__
    __asm__ __volatile__( "pushl %%ebx\n\t"
                          "movl %2,%%ebx\n\t"
                          "int $0x80\n\t"
                          "popl %%ebx"
                          : "=a" (sig)
                          : "0" (SYS_sigaction),
                            "g" (sig),
                            "c" (new),
                            "d" (old) );
#else
    __asm__ __volatile__( "int $0x80"
                          : "=a" (sig)
                          : "0" (SYS_sigaction),
                            "b" (sig),
                            "c" (new),
                            "d" (old) );
#endif  /* __PIC__ */
    if (sig>=0)
        return 0;
    errno = -sig;
    return -1;
}
#endif /* linux && __i386__ */

/* Signal stack */

static char SIGNAL_Stack[16384];
static sigset_t async_signal_set;

/**********************************************************************
 *              SIGNAL_child
 * 
 * wait4 terminated child processes
 */
static HANDLER_DEF(SIGNAL_child)
{
    HANDLER_INIT();
#ifdef HAVE_WAIT4
    wait4( 0, NULL, WNOHANG, NULL);
#elif defined (HAVE_WAITPID)
    /* I am sort-of guessing that this is the same as the wait4 call.  */
    waitpid (0, NULL, WNOHANG);
#else
    wait(NULL);
#endif
}


/**********************************************************************
 *		SIGNAL_SetHandler
 */
void SIGNAL_SetHandler( int sig, void (*func)(), int flags )
{
    int ret;

#if defined(linux) && defined(__i386__)

    struct kernel_sigaction sig_act;
    sig_act.sa_handler = func;
    sig_act.sa_flags   = SA_RESTART | (flags) ? SA_NOMASK : 0;
    sig_act.sa_mask    = 0;
    /* Point to the top of the stack, minus 4 just in case, and make
       it aligned  */
    sig_act.sa_restorer = 
        (void (*)())((int)(SIGNAL_Stack + sizeof(SIGNAL_Stack) - 4) & ~3);
    ret = wine_sigaction( sig, &sig_act, NULL );

#else  /* linux && __i386__ */

    struct sigaction sig_act;
    sig_act.sa_handler = func;
    sigemptyset( &sig_act.sa_mask );

# if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
    sig_act.sa_flags = SA_ONSTACK;
# elif defined (__svr4__) || defined(_SCO_DS)
    sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
# elif defined(__EMX__)
    sig_act.sa_flags = 0; /* FIXME: EMX has only SA_ACK and SA_SYSV */
# else
    sig_act.sa_flags = 0;
# endif
    ret = sigaction( sig, &sig_act, NULL );

#endif  /* linux && __i386__ */

    if (ret < 0)
    {
        perror( "sigaction" );
        exit(1);
    }
}

extern void stop_wait(int a);
extern void WINSOCK_sigio(int a);


/**********************************************************************
 *		SIGNAL_Init
 */
BOOL32 SIGNAL_Init(void)
{
    extern void SYNC_SetupSignals(void);

#ifdef HAVE_WORKING_SIGALTSTACK
    struct sigaltstack ss;
    ss.ss_sp    = SIGNAL_Stack;
    ss.ss_size  = sizeof(SIGNAL_Stack);
    ss.ss_flags = 0;
    if (sigaltstack(&ss, NULL) < 0)
    {
        perror("sigstack");
        return FALSE;
    }
#endif  /* HAVE_SIGALTSTACK */
    
    sigemptyset(&async_signal_set);

    SIGNAL_SetHandler( SIGCHLD, (void (*)())SIGNAL_child, 1);
#ifdef CONFIG_IPC
    sigaddset(&async_signal_set, SIGUSR2);
    SIGNAL_SetHandler( SIGUSR2, (void (*)())stop_wait, 1); 	/* For IPC */
#endif
#ifdef SIGIO
    sigaddset(&async_signal_set, SIGIO);
    SIGNAL_SetHandler( SIGIO,   (void (*)())WINSOCK_sigio, 0); 
#endif

    sigaddset(&async_signal_set, SIGALRM);

    /* ignore SIGPIPE so that WINSOCK can get a EPIPE error instead  */
    signal (SIGPIPE, SIG_IGN);

    SYNC_SetupSignals();
    return TRUE;
}


/**********************************************************************
 *              SIGNAL_MaskAsyncEvents
 */
void SIGNAL_MaskAsyncEvents( BOOL32 flag )
{
  sigprocmask( (flag) ? SIG_BLOCK : SIG_UNBLOCK , &async_signal_set, NULL);
}