Files
harbour-core/contrib/xhb/hbserv.c
Przemysław Czerpak d0be194907 2019-02-11 13:43 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* bin/commit.hb
    ! fixed UTC offset formatting

  * contrib/hbwin/win_os.prg
    * updated win_osNetRegOk() for modern MS-Windows versions:
      - on Win7 and upper set
        System\CurrentControlSet\Services\LanmanServer\Parameters\DisableLeasing
        to disable opportunistic locks.
      - do not force SMB1 to disable oplocks on Win7 and upper - new MS-Win10
        does not support SMB1 at all so this setting on the server with
        such system completely disables SMB network and forcing SMB1 on the
        client side blocks access to new Win10 servers.
        Warning! this setting is still activated on Vista so it cannot
                 connect work with new Win10 but I do not know any other
                 working method to disable oplocks in Windows Vista.
    * synced with Viktor's branch

  * contrib/xhb/hbserv.c
    ! added missing return

  * include/harbour.hbx
  * src/harbour.def
  * src/rtl/version.c
    + added new PRG functions:
         hb_osIsWin7(), hb_osIsWin8(), hb_osIsWin81(), hb_osIsWin10()

  * src/rtl/gttrm/gttrm.c
    + added autodetection for few other XTerm compatible terminals
    + respect color extension in TERM name of all XTerm compatible
      terminals

  * utils/hbmk2/hbmk2.prg
    + added support for -cpp=isoXX borowed from Viktor's branch

  * contrib/gtqtc/gtqtc.hbc
  * contrib/gtqtc/gtqtc.hbp
    * use -cpp=iso11 required for QT 5.7.0 or upper
    * extended QT detection and partial syncing with Viktor's branch
2019-02-11 13:43:40 +01:00

1094 lines
32 KiB
C

/*
* The Service/Daemon support (includes also signal/low-level error management)
*
* Copyright 2003 Giancarlo Niccolai <gian@niccolai.ws>
*
* 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, or (at your option)
* any later version.
*
* This program 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 LICENSE.txt. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapierr.h"
#include "hbapifs.h"
#include "hbvm.h"
#include "hbstack.h"
#include "hbserv.h"
#include "hbthread.h"
#include <stdio.h>
#if defined( HB_OS_WIN )
#include <windows.h>
#if defined( HB_OS_WIN_CE )
#include "hbwince.h"
#endif
#endif
/* These targets cannot compile this module */
#if ! defined( HB_OS_DOS ) && \
! defined( HB_OS_DARWIN_5 ) && \
! ( defined( HB_OS_WIN_CE ) && ( defined( __POCC__ ) || ( defined( _MSC_VER ) && ( _MSC_VER <= 1500 ) ) ) ) && \
! defined( HB_OS_WIN_64 ) && \
( ! defined( HB_OS_OS2 ) || defined( HB_OS_OS2_GCC ) ) && \
! defined( __HAIKU__ )
/* TODO: Haiku will supposedly do this later on, read /boot/develop/headers/posix/signal.h */
#if defined( HB_OS_UNIX ) || defined( HB_OS_OS2_GCC )
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#endif
#ifdef __LCC__
#define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION
#endif
/* Global definition, valid for all systems */
static void s_serviceSetHBSig( void );
static void s_serviceSetDflSig( void );
static void s_signalHandlersInit( void );
static PHB_ITEM sp_hooks = NULL;
static HB_BOOL bSignalEnabled = HB_TRUE;
static int sb_isService = 0;
/* There is a service mutex in multithreading */
static HB_CRITICAL_NEW( s_ServiceMutex );
/* This structure holds a translation to transform a certain OS level signal
into abstract HB_SIGNAL; os specific implementation must provide the
s_sigTable containing all the available translations */
typedef struct
{
HB_UINT sig;
HB_UINT subsig;
HB_UINT translated;
} S_TUPLE;
static int s_translateSignal( HB_UINT sig, HB_UINT subsig );
/* Unix specific signal handling implementation
*
* This section has unix specific code to manage the
* signals, both from kernel or from users.
*/
#if defined( HB_OS_UNIX ) || defined( HB_OS_OS2_GCC )
/* TODO: Register the old signal action to allow graceful fallback */
#if 0
static struct sigaction s_aOldAction[ SIGUSR2 + 1 ];
#endif
/* Implementation of the signal translation table */
static S_TUPLE s_sigTable[] =
{
{ SIGHUP, 0, HB_SIGNAL_REFRESH },
{ SIGINT, 0, HB_SIGNAL_INTERRUPT },
{ SIGQUIT, 0, HB_SIGNAL_QUIT },
{ SIGILL, 0, HB_SIGNAL_FAULT },
{ SIGABRT, 0, HB_SIGNAL_QUIT },
{ SIGFPE, 0, HB_SIGNAL_MATHERR },
{ SIGSEGV, 0, HB_SIGNAL_FAULT },
{ SIGTERM, 0, HB_SIGNAL_QUIT },
{ SIGUSR1, 0, HB_SIGNAL_USER1 },
{ SIGUSR2, 0, HB_SIGNAL_USER2 },
{ 0, 0, 0 }
};
#if defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ )
static void s_signalHandler( int sig )
#else
static void s_signalHandler( int sig, siginfo_t * info, void * v )
#endif
{
HB_UINT uiSig;
HB_SIZE nPos;
#if ! ( defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ ) )
HB_SYMBOL_UNUSED( v );
#endif
/* let's find the right signal handler. */
hb_threadEnterCriticalSectionGC( &s_ServiceMutex );
/* avoid working if PRG signal handling has been disabled */
if( ! bSignalEnabled )
{
hb_threadLeaveCriticalSection( &s_ServiceMutex );
return;
}
bSignalEnabled = HB_FALSE;
nPos = hb_arrayLen( sp_hooks );
/* subsig not necessary */
uiSig = ( HB_UINT ) s_translateSignal( ( HB_UINT ) sig, 0 );
while( nPos > 0 )
{
PHB_ITEM pFunction;
HB_UINT uiMask;
pFunction = hb_arrayGetItemPtr( sp_hooks, nPos );
uiMask = ( HB_UINT ) hb_arrayGetNI( pFunction, 1 );
if( uiMask & uiSig )
{
PHB_ITEM pExecArray, pRet;
int iRet;
/* we don't unlock the mutex now, even if it is
a little dangerous. But we are in a signal hander...
for now just 2 parameters */
pExecArray = hb_itemArrayNew( 3 );
hb_arraySet( pExecArray, 1, hb_arrayGetItemPtr( pFunction, 2 ) );
hb_arraySetNI( pExecArray, 2, uiSig );
/* the third parameter is an array: */
pRet = hb_arrayGetItemPtr( pExecArray, 3 );
#if defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ )
hb_arrayNew( pRet, 1 );
#elif defined( HB_OS_BSD )
hb_arrayNew( pRet, info ? 6 : 1 );
#else
hb_arrayNew( pRet, 6 );
#endif
hb_arraySetNI( pRet, HB_SERVICE_OSSIGNAL, sig );
#if ! ( defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ ) )
#if defined( HB_OS_BSD )
if( info )
#endif
{
hb_arraySetNI( pRet, HB_SERVICE_OSSUBSIG, info->si_code );
#if ! defined( HB_OS_VXWORKS )
hb_arraySetNI( pRet, HB_SERVICE_OSERROR, info->si_errno );
hb_arraySetPtr( pRet, HB_SERVICE_ADDRESS, ( void * ) info->si_addr );
hb_arraySetNI( pRet, HB_SERVICE_PROCESS, info->si_pid );
hb_arraySetNI( pRet, HB_SERVICE_UID, info->si_uid );
#endif
}
#endif
pRet = hb_itemDo( pExecArray, 0 );
iRet = hb_itemGetNI( pRet );
hb_itemRelease( pRet );
hb_itemRelease( pExecArray );
switch( iRet )
{
case HB_SERVICE_HANDLED:
bSignalEnabled = HB_TRUE;
hb_threadLeaveCriticalSection( &s_ServiceMutex );
return;
case HB_SERVICE_QUIT:
bSignalEnabled = HB_FALSE;
hb_threadLeaveCriticalSection( &s_ServiceMutex );
/* TODO: A service cleanup routine */
hb_vmRequestQuit();
/* Allow signals to go through pthreads */
s_serviceSetDflSig();
/* NOTICE: should be pthread_exit(0), but a bug in linuxthread prevents it:
calling pthread exit from a signal handler will cause infinite wait for
restart signal.
This solution is rude, while the other would allow clean VM termination...
but it works.
*/
exit( 0 );
}
}
nPos--;
}
bSignalEnabled = HB_TRUE;
#if 0
s_serviceSetHBSig();
#endif
#if 0
if( uiSig != HB_SIGNAL_UNKNOWN )
{
if( sa_oldAction[ sig ].sa_flags & SA_SIGINFO )
sa_oldAction[ sig ].sa_sigaction( sig, info, v );
else
sa_oldAction[ sig ].sa_handler( sig );
}
#endif
}
/* 2003 - <maurilio.longo@libero.it>
to fix as soon as thread support is ready on OS/2
*/
#if defined( HB_THREAD_SUPPORT ) && ! defined( HB_OS_OS2 )
static void * s_signalListener( void * my_stack )
{
static HB_BOOL bFirst = HB_TRUE;
sigset_t passall;
HB_STACK * pStack = ( HB_STACK * ) my_stack;
#if defined( HB_OS_BSD )
int sig;
#else
siginfo_t sinfo;
#endif
#ifdef HB_THREAD_TLS_KEYWORD
hb_thread_stack = my_stack;
#else
pthread_setspecific( hb_pkCurrentStack, my_stack );
#endif
pStack->th_id = HB_CURRENT_THREAD();
hb_threadLinkStack( pStack );
HB_STACK_LOCK;
/* and now accepts all signals */
sigemptyset( &passall );
/* and wait for all signals */
sigaddset( &passall, SIGHUP );
sigaddset( &passall, SIGQUIT );
sigaddset( &passall, SIGILL );
sigaddset( &passall, SIGABRT );
sigaddset( &passall, SIGFPE );
sigaddset( &passall, SIGSEGV );
sigaddset( &passall, SIGTERM );
sigaddset( &passall, SIGUSR1 );
sigaddset( &passall, SIGUSR2 );
sigaddset( &passall, SIGHUP );
pthread_cleanup_push( hb_threadTerminator, my_stack );
pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, NULL );
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
for( ;; )
{
/* allow safe cancelation */
HB_STACK_UNLOCK;
/* reset signal handling; this is done here (so I don't
mangle with pthread_ calls, and I don't hold mutexes),
and just once (doing it twice would be useless). */
if( bFirst )
{
pthread_sigmask( SIG_SETMASK, &passall, NULL );
bFirst = HB_FALSE;
}
/* This is also a cancelation point. When the main thread
is done, it will kill all the threads having a stack
including this one.
ATM we don't care very much about signal handling during
termination: no handler is set for them, so the DFL
action is taken (and that should be fine). */
#if defined( HB_OS_BSD )
sigwait( &passall, &sig );
#else
sigwaitinfo( &passall, &sinfo );
#endif
/* lock stack before passing the ball to VM. */
HB_STACK_LOCK;
#if defined( HB_OS_BSD )
s_signalHandler( sig, NULL, NULL );
#else
s_signalHandler( sinfo.si_signo, &sinfo, NULL );
#endif
}
pthread_cleanup_pop( 1 );
return 0;
}
#endif
#endif
/* Windows specific exception filter system.
*
* Windows will only catch exceptions; It is necessary to rely on the
* hb_ServiceLoop() to receive user generated messages.
*/
#ifdef HB_OS_WIN
static void s_serviceSetHBSig( void );
/* message filter hook for user generated signals */
static HHOOK s_hMsgHook = NULL;
/* old error mode */
static UINT s_uiErrorMode = 0;
/* implementation of the signal translation table
Under windows, 0 is a system exception, while 1 is a user message
*/
static S_TUPLE s_sigTable[] = {
/* memory/processor fault exception */
{ 0, EXCEPTION_ACCESS_VIOLATION, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_ILLEGAL_INSTRUCTION, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_IN_PAGE_ERROR, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_STACK_OVERFLOW, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_PRIV_INSTRUCTION, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, HB_SIGNAL_FAULT },
{ 0, EXCEPTION_DATATYPE_MISALIGNMENT, HB_SIGNAL_FAULT },
/* Math exceptions */
{ 0, EXCEPTION_FLT_DENORMAL_OPERAND, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_INVALID_OPERATION, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_INEXACT_RESULT, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_DIVIDE_BY_ZERO, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_OVERFLOW, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_STACK_CHECK, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_FLT_UNDERFLOW, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_INT_DIVIDE_BY_ZERO, HB_SIGNAL_MATHERR },
{ 0, EXCEPTION_INT_OVERFLOW, HB_SIGNAL_MATHERR },
/* User requests */
{ 1, WM_USER, HB_SIGNAL_USER1 },
{ 1, WM_USER + 1, HB_SIGNAL_USER2 },
{ 1, WM_USER + 2, HB_SIGNAL_REFRESH },
{ 1, WM_USER + 3, HB_SIGNAL_INTERRUPT },
{ 1, WM_QUIT, HB_SIGNAL_QUIT },
/* Console handler */
{ 2, CTRL_C_EVENT, HB_SIGNAL_INTERRUPT },
{ 2, CTRL_BREAK_EVENT, HB_SIGNAL_INTERRUPT },
{ 2, CTRL_CLOSE_EVENT, HB_SIGNAL_QUIT },
{ 2, CTRL_LOGOFF_EVENT, HB_SIGNAL_QUIT },
{ 2, CTRL_SHUTDOWN_EVENT, HB_SIGNAL_QUIT },
{ 0, 0, 0 }
};
/* Manager of signals for windows */
static LONG s_signalHandler( int type, int sig, PEXCEPTION_RECORD exc )
{
HB_SIZE nPos;
HB_UINT uiSig;
/* let's find the right signal handler. */
hb_threadEnterCriticalSectionGC( &s_ServiceMutex );
/* avoid working if PRG signal handling has been disabled */
if( ! bSignalEnabled )
{
hb_threadLeaveCriticalSection( &s_ServiceMutex );
return EXCEPTION_EXECUTE_HANDLER;
}
bSignalEnabled = HB_FALSE;
nPos = hb_arrayLen( sp_hooks );
/* subsig not necessary */
uiSig = ( HB_UINT ) s_translateSignal( ( HB_UINT ) type, ( HB_UINT ) sig );
while( nPos > 0 )
{
PHB_ITEM pFunction;
HB_UINT uiMask;
pFunction = hb_arrayGetItemPtr( sp_hooks, nPos );
uiMask = ( HB_UINT ) hb_arrayGetNI( pFunction, 1 );
if( ( uiMask & uiSig ) == uiSig )
{
PHB_ITEM pExecArray, pRet;
int iRet;
/* we don't unlock the mutex now, even if it is
a little dangerous. But we are in a signal hander...
for now just 2 parameters */
pExecArray = hb_itemArrayNew( 3 );
hb_arraySetForward( pExecArray, 1, hb_arrayGetItemPtr( pFunction, 2 ) );
hb_arraySetNI( pExecArray, 2, uiSig );
/* the third parameter is an array:
* 1: low-level signal
* 2: low-level subsignal
* 3: low-level system error
* 4: address that rose the signal
* 5: process id of the signal riser
* 6: UID of the riser
*/
pRet = hb_arrayGetItemPtr( pExecArray, 3 );
hb_arrayNew( pRet, 6 );
hb_arraySetNI( pRet, HB_SERVICE_OSSIGNAL, type );
hb_arraySetNI( pRet, HB_SERVICE_OSSUBSIG, sig );
/* could be meaningless, but does not matter here */
hb_arraySetNI( pRet, HB_SERVICE_OSERROR, GetLastError() );
if( type == 0 ) /* exception */
hb_arraySetPtr( pRet, HB_SERVICE_ADDRESS, ( void * ) exc->ExceptionAddress );
else
hb_arraySetPtr( pRet, HB_SERVICE_ADDRESS, NULL );
/* TODO: */
hb_arraySetNI( pRet, HB_SERVICE_PROCESS, GetCurrentThreadId() );
/* TODO: */
hb_arraySetNI( pRet, HB_SERVICE_UID, 0 );
pRet = hb_itemDo( pExecArray, 0 );
iRet = hb_itemGetNI( pRet );
hb_itemRelease( pRet );
hb_itemRelease( pExecArray );
switch( iRet )
{
case HB_SERVICE_HANDLED:
bSignalEnabled = HB_TRUE;
hb_threadLeaveCriticalSection( &s_ServiceMutex );
return EXCEPTION_CONTINUE_EXECUTION;
case HB_SERVICE_QUIT:
bSignalEnabled = HB_FALSE;
hb_threadLeaveCriticalSection( &s_ServiceMutex );
hb_vmRequestQuit();
#ifndef HB_THREAD_SUPPORT
hb_vmQuit();
exit( 0 );
#else
hb_threadCancelInternal();
#endif
}
}
nPos--;
}
bSignalEnabled = HB_TRUE;
return EXCEPTION_EXECUTE_HANDLER;
}
static LRESULT CALLBACK s_exceptionFilter( PEXCEPTION_POINTERS exInfo )
{
return s_signalHandler( 0, exInfo->ExceptionRecord->ExceptionCode, exInfo->ExceptionRecord );
}
static LRESULT CALLBACK s_MsgFilterFunc( int nCode, WPARAM wParam, LPARAM lParam )
{
PMSG msg;
if( nCode < 0 )
return CallNextHookEx( s_hMsgHook, nCode, wParam, lParam );
msg = ( PMSG ) lParam;
switch( msg->message )
{
case WM_USER:
case WM_USER + 1:
case WM_USER + 2:
case WM_USER + 3:
case WM_QUIT:
/* we'll ignore the request here.
the application must still receive the message */
s_signalHandler( 1, msg->message, NULL );
}
/* return next hook anyway */
return CallNextHookEx( s_hMsgHook, nCode, wParam, lParam );
}
#ifdef HB_THREAD_SUPPORT
extern DWORD hb_dwCurrentStack;
#endif
BOOL WINAPI s_ConsoleHandlerRoutine( DWORD dwCtrlType )
{
#ifdef HB_THREAD_SUPPORT
HB_STACK * pStack = NULL;
/* we need a new stack: this is NOT an hb thread. */
if( TlsGetValue( hb_dwCurrentStack ) == 0 )
{
pStack = hb_threadCreateStack( GetCurrentThreadId() );
pStack->th_h = GetCurrentThread();
TlsSetValue( hb_dwCurrentStack, ( void * ) pStack );
}
#endif
s_signalHandler( 2, dwCtrlType, NULL );
#ifdef HB_THREAD_SUPPORT
if( pStack )
hb_threadDestroyStack( pStack );
#endif
/* We have handled it */
return TRUE;
}
#endif
/**
* Filter/handlers setup/shutdown
* This utility functions are meant to abstract the process of declare and
* remove the signal handlers, and do it in a mutlti-platform fashion. Use this
* to implement new platform signal/exception handlers.
*/
/* Set the signal handlers to our program interceptors. */
static void s_serviceSetHBSig( void )
{
#if defined( HB_OS_UNIX ) || defined( HB_OS_OS2_GCC )
struct sigaction act;
#if defined( HB_THREAD_SUPPORT ) && ! defined( HB_OS_OS2 )
sigset_t blockall;
/* set signal mask */
sigemptyset( &blockall );
sigaddset( &blockall, SIGHUP );
sigaddset( &blockall, SIGQUIT );
sigaddset( &blockall, SIGILL );
sigaddset( &blockall, SIGABRT );
sigaddset( &blockall, SIGFPE );
sigaddset( &blockall, SIGSEGV );
sigaddset( &blockall, SIGTERM );
sigaddset( &blockall, SIGUSR1 );
sigaddset( &blockall, SIGUSR2 );
sigaddset( &blockall, SIGHUP );
pthread_sigmask( SIG_SETMASK, &blockall, NULL );
#endif
/* to avoid problems with differ sigaction structures and uninitialized
fields */
memset( &act, 0, sizeof( act ) );
#if defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ )
act.sa_handler = s_signalHandler;
#else
/* using more descriptive sa_action instead of sa_handler */
act.sa_handler = NULL; /* if act.sa.. is a union, we just clean this */
act.sa_sigaction = s_signalHandler; /* this is what matters */
/* block al signals, we don't want to be interrupted. */
#if 0
sigfillset( &act.sa_mask );
#endif
#endif
#ifdef HB_OS_OS2_GCC
act.sa_flags = SA_NOCLDSTOP;
#else
act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
#endif
sigaction( SIGHUP, &act, NULL );
sigaction( SIGQUIT, &act, NULL );
sigaction( SIGILL, &act, NULL );
sigaction( SIGABRT, &act, NULL );
sigaction( SIGFPE, &act, NULL );
sigaction( SIGSEGV, &act, NULL );
sigaction( SIGTERM, &act, NULL );
sigaction( SIGUSR1, &act, NULL );
sigaction( SIGUSR2, &act, NULL );
/* IGNORE pipe */
signal( SIGPIPE, SIG_IGN );
#endif
#ifdef HB_OS_WIN
/* disable all os-level error boxes */
s_uiErrorMode = SetErrorMode(
SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX );
SetUnhandledExceptionFilter( s_exceptionFilter );
s_hMsgHook = SetWindowsHookEx( WH_GETMESSAGE, ( HOOKPROC ) s_MsgFilterFunc, NULL, GetCurrentThreadId() );
SetConsoleCtrlHandler( s_ConsoleHandlerRoutine, TRUE );
#endif
}
/* Reset the signal handlers to the default OS value */
static void s_serviceSetDflSig( void )
{
#ifdef HB_OS_UNIX
signal( SIGHUP, SIG_DFL );
signal( SIGQUIT, SIG_DFL );
signal( SIGILL, SIG_DFL );
signal( SIGABRT, SIG_DFL );
signal( SIGFPE, SIG_DFL );
signal( SIGSEGV, SIG_DFL );
signal( SIGTERM, SIG_DFL );
signal( SIGUSR1, SIG_DFL );
signal( SIGUSR2, SIG_DFL );
signal( SIGPIPE, SIG_DFL );
#endif
#ifdef HB_OS_WIN
SetUnhandledExceptionFilter( NULL );
if( s_hMsgHook != NULL )
{
UnhookWindowsHookEx( s_hMsgHook );
s_hMsgHook = NULL;
}
SetErrorMode( s_uiErrorMode );
SetConsoleCtrlHandler( s_ConsoleHandlerRoutine, FALSE );
#endif
}
/* This translates a signal into abstract HB_SIGNAL
from os specific representation */
static int s_translateSignal( HB_UINT sig, HB_UINT subsig )
{
int i = 0;
while( s_sigTable[ i ].sig != 0 || s_sigTable[ i ].subsig != 0 || s_sigTable[ i ].translated != 0 )
{
if( s_sigTable[ i ].sig == sig &&
( s_sigTable[ i ].subsig == subsig || s_sigTable[ i ].subsig == 0 ) )
{
return s_sigTable[ i ].translated;
}
i++;
}
return HB_SIGNAL_UNKNOWN;
}
static void hb_service_exit( void * cargo )
{
HB_SYMBOL_UNUSED( cargo );
hb_serviceExit();
}
/**
* Initializes signal handler system
*/
static void s_signalHandlersInit()
{
#if defined( HB_THREAD_SUPPORT ) && ( defined( HB_OS_UNIX ) || defined( HB_OS_UNIX ) )
pthread_t res;
HB_STACK * pStack;
s_serviceSetHBSig();
pStack = hb_threadCreateStack( 0 );
pthread_create( &res, NULL, s_signalListener, pStack );
#else
s_serviceSetHBSig();
#endif
sp_hooks = hb_itemNew( NULL );
hb_arrayNew( sp_hooks, 0 );
hb_vmAtQuit( hb_service_exit, NULL );
}
/**
* hb_*Service routines
* This is the core of the service system.
*/
/**
* Starts the service system.
* Initializes the needed variables.
* On unix: if the parameter is .T., puts the server in daemonic mode, detaching
* the main thread from the console and terminating it.
*/
HB_FUNC( HB_STARTSERVICE )
{
#ifdef HB_THREAD_SUPPORT
int iCount = hb_threadCountStacks();
if( iCount > 2 || ( sp_hooks == NULL && iCount > 1 ) )
{
/* TODO: Right error code here */
hb_errRT_BASE_SubstR( EG_ARG, 3012, "Service must be started before starting threads", NULL, 0 );
return;
}
#endif
#if defined( HB_OS_UNIX ) && ! defined( HB_OS_VXWORKS )
{
/* Iconic? */
if( hb_parl( 1 ) )
{
int pid = fork();
if( pid != 0 )
{
hb_vmRequestQuit();
return;
}
#ifdef HB_THREAD_SUPPORT
#ifdef HB_THREAD_TLS_KEYWORD
hb_thread_stack = &hb_stackMT;
#else
pthread_setspecific( hb_pkCurrentStack, ( void * ) &hb_stackMT );
#endif
#endif
}
}
#endif
/* let's begin */
sb_isService = HB_TRUE;
/* in windows, we just detach from console */
#ifdef HB_OS_WIN
if( hb_parl( 1 ) )
FreeConsole();
#endif
/* Initialize only if the service has not yet been initialized */
if( sp_hooks == NULL )
s_signalHandlersInit();
}
/**
* Returns true if the current program is a service, that is if hb_StartService() has
* Been called. C version useful for internal api
*/
HB_BOOL hb_isService( void )
{
return sb_isService;
}
/**
* Clean up when system exits
* Called from hb_vmQuit()
*/
void hb_serviceExit( void )
{
if( sp_hooks != NULL )
{
/* reset default signal handling */
s_serviceSetDflSig();
hb_itemRelease( sp_hooks );
sp_hooks = NULL;
}
}
/**
* Returns true if the current program is a service, that is if hb_StartService() has
* Been called.
*/
HB_FUNC( HB_ISSERVICE )
{
hb_retl( sb_isService );
}
/**
* This is -at least- an helper functions that implements the main loop for
* the service/daemon system.
* The minimal thing to do is a hb_gcCollectAll(), because, generally, servers
* are not interactive, so they tend to have garbage to collect.
* Under windows, it peeks the pending messages and send the relevant ones
* (quit, user+1 and user+2) to our handling functions.
*/
HB_FUNC( HB_SERVICELOOP )
{
#ifdef HB_OS_WIN
MSG msg;
/* This is just here to trigger our internal hook routine, if the
final application does not any message handling.
*/
if( ! PeekMessage( &msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE ) )
{
PeekMessage( &msg, NULL, WM_USER, WM_USER + 3, PM_REMOVE );
}
#endif
hb_gcCollectAll( HB_FALSE );
}
HB_FUNC( HB_PUSHSIGNALHANDLER )
{
int iMask = hb_parni( 1 );
PHB_ITEM pFunc = hb_param( 2, HB_IT_ANY ), pHandEntry;
if( pFunc == NULL || iMask == 0 ||
( ! HB_IS_POINTER( pFunc ) && ! HB_IS_STRING( pFunc ) && ! HB_IS_BLOCK( pFunc ) )
)
{
hb_errRT_BASE_SubstR( EG_ARG, 3012, "Wrong parameter count/type", NULL,
2, hb_param( 1, HB_IT_ANY ), hb_param( 2, HB_IT_ANY ) );
return;
}
pHandEntry = hb_itemArrayNew( 2 );
hb_arraySetNI( pHandEntry, 1, iMask );
hb_arraySet( pHandEntry, 2, pFunc );
/* if the hook is not initialized, initialize it */
if( sp_hooks == NULL )
s_signalHandlersInit();
hb_threadEnterCriticalSectionGC( &s_ServiceMutex );
hb_arrayAddForward( sp_hooks, pHandEntry );
hb_threadLeaveCriticalSection( &s_ServiceMutex );
hb_itemRelease( pHandEntry );
}
HB_FUNC( HB_POPSIGNALHANDLER )
{
int nLen;
if( sp_hooks != NULL )
{
hb_threadEnterCriticalSectionGC( &s_ServiceMutex );
nLen = hb_arrayLen( sp_hooks );
if( nLen > 0 )
{
hb_arrayDel( sp_hooks, nLen );
hb_arrayDel( sp_hooks, nLen - 1 );
hb_arraySize( sp_hooks, nLen - 2 );
hb_retl( HB_TRUE );
if( hb_arrayLen( sp_hooks ) == 0 )
{
hb_itemRelease( sp_hooks );
sp_hooks = NULL; /* So it can be reinitilized */
}
}
else
hb_retl( HB_FALSE );
hb_threadLeaveCriticalSection( &s_ServiceMutex );
}
else
hb_retl( HB_FALSE );
}
/**
* Return a character description of the low-level signal that has been
* issued to signal handling routines. This is system dependent.
* TODO: Make it international through the xHarbour standard message system.
*/
HB_FUNC( HB_SIGNALDESC )
{
#if defined( HB_OS_UNIX ) || defined( HB_OS_OS2_GCC )
int iSig = hb_parni( 1 );
int iSubSig = hb_parni( 2 );
switch( iSig )
{
case SIGSEGV: switch( iSubSig )
{
#if ! defined( HB_OS_BSD ) && ! defined( HB_OS_OS2_GCC ) && ! defined( __WATCOMC__ )
case SEGV_MAPERR: hb_retc_const( "Segmentation fault: address not mapped to object" ); return;
case SEGV_ACCERR: hb_retc_const( "Segmentation fault: invalid permissions for mapped object" ); return;
#endif
default: hb_retc_const( "Segmentation fault" ); return;
}
case SIGILL: switch( iSubSig )
{
#if ! defined( HB_OS_BSD ) && ! defined( HB_OS_OS2_GCC ) && ! defined( __WATCOMC__ )
case ILL_ILLOPC: hb_retc_const( "Illegal operation: illegal opcode" ); return;
case ILL_ILLOPN: hb_retc_const( "Illegal operation: illegal operand" ); return;
case ILL_ILLADR: hb_retc_const( "Illegal operation: illegal addressing mode" ); return;
case ILL_ILLTRP: hb_retc_const( "Illegal operation: illegal trap" ); return;
case ILL_PRVOPC: hb_retc_const( "Illegal operation: privileged opcode" ); return;
case ILL_PRVREG: hb_retc_const( "Illegal operation: privileged register" ); return;
case ILL_COPROC: hb_retc_const( "Illegal operation: coprocessor error" ); return;
case ILL_BADSTK: hb_retc_const( "Illegal operation: internal stack error" ); return;
#endif
default: hb_retc_const( "Illegal operation" ); return;
}
case SIGFPE: switch( iSubSig )
{
#if ! defined( HB_OS_OS2_GCC ) && ! defined( __WATCOMC__ )
#if ! defined( HB_OS_DARWIN )
case FPE_INTDIV: hb_retc_const( "Floating point: integer divide by zero" ); return;
case FPE_INTOVF: hb_retc_const( "Floating point: integer overflow" ); return;
#endif
case FPE_FLTDIV: hb_retc_const( "Floating point: floating point divide by zero" ); return;
case FPE_FLTOVF: hb_retc_const( "Floating point: floating point overflow" ); return;
case FPE_FLTUND: hb_retc_const( "Floating point: floating point underflow" ); return;
case FPE_FLTRES: hb_retc_const( "Floating point: floating point inexact result" ); return;
#if ! defined( HB_OS_VXWORKS )
case FPE_FLTINV: hb_retc_const( "Floating point: floating point invalid operation" ); return;
#endif
#if ! defined( HB_OS_DARWIN )
case FPE_FLTSUB: hb_retc_const( "Floating point: subscript out of range" ); return;
#endif
#endif
default: hb_retc_const( "Floating point" ); return;
}
case SIGQUIT:
hb_retc_const( "Quit" );
return;
case SIGHUP:
hb_retc_const( "Update" );
return;
case SIGINT:
hb_retc_const( "Interrupt" );
return;
case SIGPIPE:
hb_retc_const( "Broken pipe" );
return;
case SIGTERM:
hb_retc_const( "Terminate process" );
return;
case SIGABRT:
hb_retc_const( "Abort" );
return;
case SIGUSR1:
hb_retc_const( "User defined" );
return;
case SIGUSR2:
hb_retc_const( "User defined (secondary)" );
return;
}
#elif defined( HB_OS_WIN )
int iSig = hb_parni( 1 );
if( iSig == 0 ) /* exception */
{
DWORD dwSubSig = ( DWORD ) hb_parnl( 2 );
switch( dwSubSig )
{
case EXCEPTION_ACCESS_VIOLATION:
hb_retc_const( "Memory read/write access violation" ); return;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
hb_retc_const( "Array out of bounds" ); return;
case EXCEPTION_DATATYPE_MISALIGNMENT:
hb_retc_const( "Data misaligned" ); return;
case EXCEPTION_FLT_DENORMAL_OPERAND:
hb_retc_const( "Denormal operand in Floating-point operation" ); return;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
hb_retc_const( "Floating-point division by zero" ); return;
case EXCEPTION_FLT_INEXACT_RESULT:
hb_retc_const( "Inexact floating-point operation result" ); return;
case EXCEPTION_FLT_INVALID_OPERATION:
hb_retc_const( "Invalid floating-point operation" ); return;
case EXCEPTION_FLT_OVERFLOW:
hb_retc_const( "Floating-point numeric overflow" ); return;
case EXCEPTION_FLT_STACK_CHECK:
hb_retc_const( "Floating-point out of stack" ); return;
case EXCEPTION_FLT_UNDERFLOW:
hb_retc_const( "Floating-point numeric underflow" ); return;
case EXCEPTION_ILLEGAL_INSTRUCTION:
hb_retc_const( "Illegal instruction" ); return;
case EXCEPTION_IN_PAGE_ERROR:
hb_retc_const( "Paging error" ); return;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
hb_retc_const( "Integer division by zero" ); return;
case EXCEPTION_INT_OVERFLOW:
hb_retc_const( "Integer numeric overflow" ); return;
case EXCEPTION_PRIV_INSTRUCTION:
hb_retc_const( "Illegal instruction for current machine mode" ); return;
case EXCEPTION_STACK_OVERFLOW:
hb_retc_const( "Stack overflow" ); return;
}
}
#endif
hb_retc_const( "Unrecognized signal" );
}
/*****************************************************************************
* Debug help: generates a fault or a math error to see if signal catching
* is working
**************************************/
HB_FUNC( HB_SERVICEGENERATEFAULT )
{
int * pGPF = NULL;
*pGPF = 0;
/* if it doesn't cause GPF (on some platforms it's possible) try this */
*( --pGPF ) = 0;
}
HB_FUNC( HB_SERVICEGENERATEFPE )
{
static double a = 100.0, b = 0.0;
a = a / b;
}
#else
HB_FUNC( HB_STARTSERVICE ) {}
HB_FUNC( HB_ISSERVICE ) {}
HB_FUNC( HB_SERVICELOOP ) {}
HB_FUNC( HB_PUSHSIGNALHANDLER ) {}
HB_FUNC( HB_POPSIGNALHANDLER ) {}
HB_FUNC( HB_SIGNALDESC ) {}
HB_FUNC( HB_SERVICEGENERATEFAULT ) {}
HB_FUNC( HB_SERVICEGENERATEFPE ) {}
#endif