/*
 * Copyright(C) 2010 Крипто-Про, ООО
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто-Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто-Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 224475 $
 * \date $Date:: 2021-01-28 19:02:41 +0300#$
 * \author $Author: maxdm $
 *
 * \brief Общая часть обёрток, которые обеспечивают 
 *        защиту от сигналов и имперсонализацию. 
 *        Как для блокирующихся функций, так и для неблокирующихся.
 *		
 *		reader/sup_dlfcn.h
 *		reader/sup_fcntl.h
 *		reader/sup_pwd_grp.h
 *		reader/sup_signal.h
 *		reader/sup_stropts.h
 *		reader/sup_sys_socket.h
 *		reader/sup_sys_stat.h
 *		reader/sup_unistd.h
 * EET_db_ctx сделали для того что бы можно было подсунуть свой контекст логера.
 * если он не определен то будет пользоваться db_ctx
 */

#ifndef sup_signal_nosig_h_
#define sup_signal_nosig_h_

#ifdef UNIX
    #include <pthread.h>
#endif
#include <signal.h>

#include "reader/support.h"
#include "reader/sup_errno.h"

#if defined USE_DBTTRACE_LOG
    #include "reader/dprint.h"

    #if !defined(EET_db_ctx)
        #define EET_db_ctx          db_ctx
    #endif
    #define EET_FTEXT(c,A)              FTEXT(c,A)
    #define EET_FTEXT2(c,A,B)           FTEXT2(c,A,B)
    #define EET_FTEXT3(c,A,B,C)         FTEXT3(c,A,B,C)
    #define EET_FTEXT4(c,A,B,C,D)       FTEXT4(c,A,B,C,D)
    #define EET_FTEXT5(c,A,B,C,D,E)     FTEXT5(c,A,B,C,D,E)
#elif defined USE_SIMPLE_LOG	// Для применений, когда нет возможности
    				// обращения к имитатору реестра
    #include "reader/sup_syslog.h"

    #if !defined DbTrace
        #define EagnEintrEval(...)  __VA_ARGS__
        #define DbTrace(x,y)        support_syslog(x, EagnEintrEval y)
        #define DB_ERROR            SUPPORT_LOG_ERR|SUPPORT_LOG_AUTH
        #define DB_WARN             SUPPORT_LOG_DEBUG|SUPPORT_LOG_AUTH /* SUPPORT_LOG_WARNING|SUPPORT_LOG_AUTH */
        #define DB_LOG              SUPPORT_LOG_NOTICE|SUPPORT_LOG_AUTH
        #define DB_CALL             SUPPORT_LOG_DEBUG|SUPPORT_LOG_AUTH
        #define DB_TRACE            SUPPORT_LOG_DEBUG|SUPPORT_LOG_AUTH
    #else
        #error "DbTrace(x,y) defined, use USE_DBTTRACE_LOG with \
		predefined EET_db_ctx, if needed. Or don't include \
		'dbprint.h'"
    #endif

    #if !defined(EET_db_ctx)
        #define EET_db_ctx          db_ctx
    #endif
    #define EET_FTEXT(c,A)              A
    #define EET_FTEXT2(c,A,B)           A B
    #define EET_FTEXT3(c,A,B,C)         A B C
    #define EET_FTEXT4(c,A,B,C,D)       A B C D
    #define EET_FTEXT5(c,A,B,C,D,E)     A B C D E
#elif defined USE_WITHOUT_LOG
    #if !defined DbTrace
        #define DbTrace(x,y)
        #define DB_ERROR
        #define DB_WARN
        #define DB_LOG
        #define DB_CALL
        #define DB_TRACE
    #else
        #error "DbTrace(x,y) defined, use USE_DBTTRACE_LOG with predefined EET_db_ctx,\
                if needed. Or don't include 'dbprint.h'"
    #endif

    #define EET_db_ctx          notused
    #define EET_FTEXT(c,A)
    #define EET_FTEXT2(c,A,B)
    #define EET_FTEXT3(c,A,B,C)
    #define EET_FTEXT4(c,A,B,C,D)
    #define EET_FTEXT5(c,A,B,C,D,E)
#else
    #error Must defined USE_DBTTRACE_LOG/USE_SIMPLE_LOG/USE_WITHOUT_LOG
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef unsigned long sup_ulong_t;

#if !defined _WIN32 
    #ifndef pthread_cleanup_push // TODO:XX так нехорошро
        #define EET_USE_CLEANUP_PUSH_POP_ROUTINE 1
    #endif
    #if EET_USE_CLEANUP_PUSH_POP_ROUTINE
	static SUP_INLINE void
	support_cleanup(void *arg)
	{
	    sigset_t *ss = (sigset_t *) arg;
	    pthread_sigmask(SIG_SETMASK, ss, NULL);
	}
    #endif

    typedef sigset_t EagnEintr_set_t;
    // TODO: typedef struct EagnEintr_state_ {
    // TODO: 	unsigned m_udetected;
    // TODO: 	unsigned m_umasked;
    // TODO:	sigset_t p_oldmask;
    // TODO: }	EagnEintr_state_t;
#else
    typedef int EagnEintr_set_t;
    // TODO: typedef struct EagnEintr_state_ {
    // TODO: 	unsigned m_udetected;
    // TODO: 	unsigned m_umasked;
    // TODO:	sigset_t p_oldmask;
    // TODO: }	EagnEintr_state_t;
#endif

static SUP_INLINE void
support_EagnEintr_mask(EagnEintr_set_t *buf, EagnEintr_set_t **ppold)
{
    #if !defined _WIN32
	sigset_t allblock;

	sigfillset(&allblock);
	*ppold = buf;
	pthread_sigmask(SIG_BLOCK, &allblock, *ppold);
	#if defined EET_USE_CLEANUP_PUSH_POP_ROUTINE
	    pthread_cleanup_push(support_cleanup, *ppold); // TODO:XX так нехорошо
	#endif
    #else
	UNUSED(buf);
	UNUSED(ppold);
    #endif
}

static SUP_INLINE void
support_EagnEintr_final(EagnEintr_set_t *pold)
{
    #if !defined _WIN32
	if (pold) {
	    pthread_sigmask(SIG_SETMASK, pold, NULL);
            #if defined EET_USE_CLEANUP_PUSH_POP_ROUTINE
		pthread_cleanup_pop(0);
	    #endif
	}
    #else
	UNUSED(pold);
    #endif
}

#define SUP_SIG_CONS_FN_(pref, func)	pref ## func

#define SUPPORT_NOSIG(type, pref, func, args, cargs, noerr, dbtrc) \
	    static SUP_INLINE type support_n_##func args \
	    { \
		type ret = (type)0xffff; \
		int serrno = -1; \
		unsigned retry = 0; \
		EagnEintr_set_t oldmask; \
		EagnEintr_set_t *pold = NULL; \
            \
		support_EagnEintr_mask(&oldmask, &pold); \
                for(; retry < 20; retry++) { \
		    SUP_SETERRNO(-1); /* FreeBSD - getpwuid_r */ \
		    ret = SUP_SIG_CONS_FN_(pref, func) cargs; \
		    if(noerr) { \
			goto ret_ret; \
		    } else { \
		        serrno = SUP_ERRNO; \
			switch(serrno) { \
			case -1: \
			    goto ret_ret; \
			case SUP_EINTR: \
			    DbTrace(DB_CALL, dbtrc); \
			    DbTrace(DB_CALL, (EET_FTEXT2(EET_db_ctx, \
				    "Info: detect %s() ", \
				    "retry=%d errno = %s(%d)"), \
				    SFUNC, retry, \
				    SUP_STRERROR(serrno), serrno)); \
			    SUP_SETERRNO(serrno); \
			    continue; \
			default: \
			    DbTrace(DB_ERROR, dbtrc);\
			    goto ret_ret; \
			} \
		    } \
		} \
		DbTrace(DB_ERROR, dbtrc); \
		DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
			"Error: EINTR/-1 retry exceeded by %s(), ", \
			"retry=%d errno = %s(%d)"), \
			SFUNC, retry, SUP_STRERROR(serrno), serrno)); \
	    ret_ret: \
		support_EagnEintr_final(pold); \
		SUP_SETERRNO(serrno); \
		return ret; \
	    }

#ifdef __cplusplus
}
#endif
#endif /* sup_signal_nosig_h_ */
