/**
*
* @File : SaveSig.cxx
*
* @Authors : D. Mathieu
*
* @Date : 20/11/2001
*
* @Version : V1.0
*
* @Synopsis :
*
**/
#include <csignal>         // sigemptyset(), sigaddset(), sigset_t
                           // SIG_BLOCK, SIG_UNBLOCK
                           // struct sigaction, _NSIG, SIGKILL, SIGSTOP

#include "CException.h"
#include "CExcFctSyst.h"
#include "nsSysteme.h"
#include "CstCodErr.h"
#include "SaveSig.h"

using namespace std;
using namespace nsUtil;   // CExcFct
using namespace nsSysteme;

// Ces deux constantes devraient être ajoutées au fichier CstCodErr.h

const int CstNumSigFaux   = 151;
const int CstSigNotDerout = 152;

namespace
{
    // La version actuelle ne supporte pas les imbrications de blocs
    //     SaveSig() .... RestaureSig(), et lève une exception
    //     CExcFct pour toute utilisation illégale de ce couple de
    //     fonctions.

    struct std::sigaction TabAction [_NSIG];
    bool ASauvegarder = true;

    struct std::sigaction TabActionIndivid [_NSIG];

    // Une variable globale (statique) est obligatoirement initialisée
    //   à 0, donc le vecteur est initialisé à faux (d'où son nom)

    bool   DejaSauvegardeIndivid [_NSIG];

    void SauverUnSignal (const sighandler_t NewHandler, int NumSig,
                         struct std::sigaction & OldAction) throw ()
    {
        struct std::sigaction Action;
        Action.sa_flags   = 0;
        Action.sa_handler = NewHandler;
        sigemptyset (& Action.sa_mask);
   
        Sigaction (NumSig, & Action, & OldAction);
   
        if (SIG_IGN == OldAction.sa_handler)
            Sigaction (NumSig, & OldAction, 0);

    } // SauverUnSignal()

    void RestaurerUnSignal (int NumSig,
                            const struct std::sigaction & OldAction)
        throw ()
    {
        Sigaction (NumSig, & OldAction, 0);

    } // RestaurerUnSignal()

    void BlockAllSig (const sigset_t & Masque) throw (CExcFctSyst)
    {
        // Boucle jusqu'à ce que Sigprocmask() ne soit plus interrompu

        for (; ;)
            try { Sigprocmask (SIG_BLOCK, & Masque, 0); break; }
            catch (...) {}

    } // BlockAllSig()

} // namespace anonyme

void nsSysteme::SaveSig (const sighandler_t NewHandler,
                         int NumSig /* = 0 */) throw (CExcFct)
{
    // Une solution beaucoup plus belle, mais non demandée, consisterait
    //   à déclarer une structure dans l'espace anonyme, dont la donnée
    //   membre serait un masque de type sigset_t, et dont le
    //   constructeur initialiserait le masque à tous les signaux
    //   bloquables.
    // Il suffirait ensuite de dééfinir dans l'espace anonyme un objet
    //   de ce type, global, qui serait donc construit une seule fois,
    //    avant l'appel de la fonction main()

    sigset_t Masque;
    sigemptyset (& Masque);
    for (int i = _NSIG; --i; )
        if (SIGKILL != i && SIGSTOP != i) sigaddset (& Masque, i);

    if (NumSig)
    {
        // Sauvegarde d'un signal unique

        if (NumSig <= 0 || NumSig >= _NSIG)
            throw CExcFct ("SaveSig : signal inexistant",
                            CstNumSigFaux);

        if (SIGKILL == NumSig || SIGSTOP == NumSig)
            throw CExcFct ("SaveSig : signal non déroutable",
                            CstSigNotDerout);

        if (DejaSauvegardeIndivid [NumSig])
            throw CExcFct ("SaveSig : signal déjà sauvegardé",
                            CstSigSaved);

        BlockAllSig (Masque);

        SauverUnSignal (NewHandler, NumSig, TabActionIndivid [NumSig]);
        DejaSauvegardeIndivid [NumSig] = true;
        Sigprocmask (SIG_UNBLOCK, & Masque, 0);
    }
    else
    {
        if (! ASauvegarder)
            throw CExcFct ("SaveSig : signaux deja sauvegardes",
                            CstSigSaved);

        // Boucle jusqu'à ce que Sigprocmask() ne soit plus interrompu

        BlockAllSig (Masque);

        for (int i = _NSIG; --i; )
            if (SIGKILL != i && SIGSTOP != i)
                SauverUnSignal (NewHandler, i, TabAction [NumSig]);
        ASauvegarder = false;
        Sigprocmask (SIG_UNBLOCK, & Masque, 0);
    }

}  //  SaveSig()
   
void nsSysteme::RestaureSig (int NumSig /* = 0 */) throw (CExcFct)
{
    sigset_t Masque;
    sigemptyset (& Masque);
    for (int i = _NSIG; --i; )
        if (SIGKILL != i && SIGSTOP != i) sigaddset (& Masque, i);

    if (NumSig)
    {
        // Restauration d'un signal unique

        if (NumSig <= 0 || NumSig >= _NSIG)
            throw CExcFct ("ReataureSig : signal inexistant",
                            CstNumSigFaux);

        if (SIGKILL == NumSig || SIGSTOP == NumSig)
            throw CExcFct ("RestaureSig : signal non déroutable",
                            CstSigNotDerout);

        if (!DejaSauvegardeIndivid [NumSig])
            throw CExcFct ("RestaureSig : signal non sauvegardé",
                            CstSigSaved);

        BlockAllSig (Masque);

        RestaurerUnSignal (NumSig, TabActionIndivid [NumSig]);
        DejaSauvegardeIndivid [NumSig] = false;
        Sigprocmask (SIG_UNBLOCK, & Masque, 0);
    }
    else
    {
        if (ASauvegarder)
            throw CExcFct ("RestaureSig : signaux non sauvegardes",
                           CstSigNotSaved);

        BlockAllSig (Masque);

        for (int i = _NSIG; --i; )
            if (SIGKILL != i && SIGSTOP != i)
                RestaurerUnSignal (NumSig, TabAction [i]);

        ASauvegarder = true;
        Sigprocmask (SIG_UNBLOCK, & Masque, 0);
    }
   
} // RestaureSig()