////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2017 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 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           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include "signal.h"
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <features.h>

//Linux signal handling article:
//http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show

typedef void (__cdecl *fnCrashHandler)(int i_iType, void *i_pContext);

struct stChContext
{
    volatile int   iInstalled;
    volatile int   iProcessed;
    fnCrashHandler pUserHandler;
};

static stChContext g_stContext = {0};


////////////////////////////////////////////////////////////////////////////////
//Get_Signal_Text
static const char *ChGetSignalText(int i_iSignal, siginfo_t* i_pSigInfo)
{
    if (i_iSignal == SIGBUS)
    {
        if (i_pSigInfo->si_code == BUS_ADRALN)      { return "SIGBUS::BUS_ADRALN"; }
        else if (i_pSigInfo->si_code == BUS_ADRERR) { return "SIGBUS::BUS_ADRERR"; }
        else if (i_pSigInfo->si_code == BUS_OBJERR) { return "SIGBUS::BUS_OBJERR"; }
        else                                        { return "SIGBUS::<unknown>";  }
    }
    else if (i_iSignal == SIGFPE)
    {
        if (i_pSigInfo->si_code == FPE_FLTDIV)      { return "SIGFPE::FPE_FLTDIV"; }
        else if (i_pSigInfo->si_code == FPE_FLTINV) { return "SIGFPE::FPE_FLTINV"; }
        else if (i_pSigInfo->si_code == FPE_FLTOVF) { return "SIGFPE::FPE_FLTOVF"; }
        else if (i_pSigInfo->si_code == FPE_FLTRES) { return "SIGFPE::FPE_FLTRES"; }
        else if (i_pSigInfo->si_code == FPE_FLTSUB) { return "SIGFPE::FPE_FLTSUB"; }
        else if (i_pSigInfo->si_code == FPE_FLTUND) { return "SIGFPE::FPE_FLTUND"; }
        else if (i_pSigInfo->si_code == FPE_INTDIV) { return "SIGFPE::FPE_INTDIV"; }
        else if (i_pSigInfo->si_code == FPE_INTOVF) { return "SIGFPE::FPE_INTOVF"; }
        else                                        { return "SIGFPE::<unknown>";  }
    }
    else if (i_iSignal == SIGILL)
    {
        if (i_pSigInfo->si_code == ILL_BADSTK)      { return "SIGILL::ILL_BADSTK"; }
        else if (i_pSigInfo->si_code == ILL_COPROC) { return "SIGILL::ILL_COPROC"; }
        else if (i_pSigInfo->si_code == ILL_ILLOPN) { return "SIGILL::ILL_ILLOPN"; }
        else if (i_pSigInfo->si_code == ILL_ILLADR) { return "SIGILL::ILL_ILLADR"; }
        else if (i_pSigInfo->si_code == ILL_ILLTRP) { return "SIGILL::ILL_ILLTRP"; }
        else if (i_pSigInfo->si_code == ILL_PRVOPC) { return "SIGILL::ILL_PRVOPC"; }
        else if (i_pSigInfo->si_code == ILL_PRVREG) { return "SIGILL::ILL_PRVREG"; }
        else                                        { return "SIGILL::<unknown>";  }
    }
    else if (i_iSignal == SIGSEGV)
    {
        if (i_pSigInfo->si_code == SEGV_MAPERR)     { return "SIGSEGV::SEGV_MAPERR"; }
        else if (i_pSigInfo->si_code == SEGV_ACCERR){ return "SIGSEGV::SEGV_ACCERR"; }
        else                                        { return "SIGSEGV::<unknown>";   }
    }
    
    return "<unknown>::<unknown>"; 
}//Get_Signal_Text


////////////////////////////////////////////////////////////////////////////////
//ChHandler
static void ChHandler(int i_iSignal, siginfo_t *i_pSigInfo, void *i_pContext)
{
    if (g_stContext.iProcessed)
    {
        return;
    }

    g_stContext.iProcessed = 1;

    if (g_stContext.pUserHandler)
    {
        g_stContext.pUserHandler(0, NULL);
    }

    printf("Process has been terminated by signal {%s}\n",
           ChGetSignalText(i_iSignal, i_pSigInfo));
}//ChHandler


////////////////////////////////////////////////////////////////////////////////
//ChInstall
static tBOOL ChInstall(fnCrashHandler i_fnHandler)
{
    struct sigaction l_sSignal;
    struct sigaction l_sSignalPipe;
    tBOOL            l_bReturn  = TRUE;

    if (g_stContext.iInstalled)
    {
        return FALSE;
    }

    g_stContext.iProcessed   = 0;
    g_stContext.pUserHandler = i_fnHandler;

    ////////////////////////////////////////////////////////////////////////////
    // installing singla handilgn ...
    memset(&l_sSignalPipe, 0, sizeof(l_sSignalPipe));
    memset(&l_sSignal, 0, sizeof(l_sSignal));

    //remove SIGPIPE signal, should be ignored
    l_sSignalPipe.sa_handler = SIG_IGN;
    sigemptyset(&l_sSignalPipe.sa_mask);
    l_bReturn = (sigaction(SIGPIPE, &l_sSignalPipe, NULL) == 0);

    l_sSignal.sa_flags = SA_RESETHAND | SA_SIGINFO;
    l_sSignal.sa_sigaction = &ChHandler;
    sigemptyset(&l_sSignal.sa_mask);

    l_bReturn &= (sigaction(SIGILL,  &l_sSignal, NULL) == 0);
    l_bReturn &= (sigaction(SIGABRT, &l_sSignal, NULL) == 0);
    l_bReturn &= (sigaction(SIGFPE,  &l_sSignal, NULL) == 0);
    l_bReturn &= (sigaction(SIGBUS,  &l_sSignal, NULL) == 0);
    l_bReturn &= (sigaction(SIGSEGV, &l_sSignal, NULL) == 0);

    if (l_bReturn)
    {
        g_stContext.iInstalled = 1;
    }
    else
    {
        printf("ERROR: can't initialize signal handler");
    }
    
    return l_bReturn;
}//ChInstall


////////////////////////////////////////////////////////////////////////////////
//ChUnInstall
static tBOOL ChUnInstall()
{
    return TRUE;
}//ChUnInstall
