Files
harbour-core/harbour/contrib/xhb/hbserv.c
Przemyslaw Czerpak 9723beb5c0 2012-03-13 12:55 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* harbour/package/harbour.spec
    ! added missing attribute for harbour.ld.conf file
    ! added optional hbhttpds library

  * harbour/contrib/hbct/envparam.c
  * harbour/contrib/hbct/dummy.c
  * harbour/contrib/hbct/hbct.hbp
  * harbour/contrib/hbct/hbct.hbx
    + added ENVPARM() - now it's implemented for all platforms but
      it should be tested on different *nixes - some of them may not
      support 'char * environ' public variable

  * harbour/contrib/xhb/hbserv.c
    * added missing dummy functions in DOS builds

  * harbour/utils/hbmk2/hbmk2.prg
    ! do not set binding to hb_forceLinkMainWin() when -nohblib and -gui
      switches are used together


  * harbour/include/hbapicdp.h
  * harbour/src/rtl/cdpapi.c
    + added new C functions hb_cdpnDupUpper(), hb_cdpnDupLower(),
      hb_cdpnDup2Upper() and hb_cdpnDup2Lower().
      They should be used instead of hb_strUpper() and hb_strLower()
      functions because they can work well with CPs where upper and
      lower characters uses different number of bytes, i.e. UTF8 has
      such characters.

  * harbour/src/rtl/strcase.c
  * harbour/src/rtl/fstemp.c
  * harbour/src/rtl/filesys.c
  * harbour/src/rdd/dbfcdx/dbfcdx1.c
  * harbour/src/rdd/hbsix/sxsem.c
    * replaced obsolete hb_strUpper() and hb_strLower() functions with
      new hb_cdpnDup*Upper()/hb_cdpnDup*Lower() ones.

  * harbour/src/rdd/hbsix/sxutil.c
    * replaced obsolete hb_charUpper() function with hb_cdpnDupUpper()

  * harbour/contrib/sddpg/sddpg.c
  * harbour/contrib/sddsqlt3/sddsqlt3.c
  * harbour/contrib/sddmy/sddmy.c
  * harbour/contrib/sddfb/sddfb.c
  * harbour/contrib/sddodbc/sddodbc.c
  * harbour/contrib/sddoci/sddoci.c
    ! fixed memory leaks
    * replaced obsolete hb_strUpper() and hb_strLower() functions with
      new hb_cdpnDup*Upper()/hb_cdpnDup*Lower() ones.
    ; TODO: these conversions are redundant - I'll remove them in next
            commit.
2012-03-13 11:55:43 +00:00

1120 lines
33 KiB
C

/*
* $Id$
*/
/*
* xHarbour Project source code:
* The Service/Daemon support
* (Includes also signal/low level error management)
*
* Copyright 2003 Giancarlo Niccolai [gian@niccolai.ws]
* www - http://www.xharbour.org
*
* 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 software; see the file COPYING. if not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, xHarbour license gives permission for
* additional uses of the text contained in its release of xHarbour.
*
* The exception is that, if you link the xHarbour 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 xHarbour 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 with this xHarbour
* explicit exception. if you add/copy code from other sources,
* as the General public License permits, the above 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 xHarbour, 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 can't 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
/* TODO:
we'll use hb_fsPopen for popening once we put it multiplatform. For now: */
#ifdef HB_OS_WIN
#define popen _popen
#define pclose _pclose
#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
static struct sigaction sa_oldAction[ SIGUSR2 + 1 ]; */
/* 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 uiMask;
HB_UINT uiSig;
PHB_ITEM pFunction, pExecArray, pRet;
HB_SIZE nPos;
int iRet;
#if !( defined( HB_OS_OS2_GCC ) || defined( __WATCOMC__ ) )
HB_SYMBOL_UNUSED( v );
#endif
/* let's find the right signal handler. */
hb_threadEnterCriticalSection( &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 )
{
pFunction = hb_arrayGetItemPtr( sp_hooks, nPos );
uiMask = ( HB_UINT ) hb_arrayGetNI( pFunction, 1 );
if( uiMask & uiSig )
{
/* 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;
/*s_serviceSetHBSig();*/
/* TODO
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 );
}
*/
}
/* 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 )
{
PHB_ITEM pFunction, pExecArray, pRet;
HB_SIZE nPos;
HB_UINT uiSig, uiMask;
int iRet;
/* let's find the right signal handler. */
hb_threadEnterCriticalSection( &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 )
{
pFunction = hb_arrayGetItemPtr( sp_hooks, nPos );
uiMask = ( HB_UINT ) hb_arrayGetNI( pFunction, 1 );
if( ( uiMask & uiSig ) == uiSig )
{
/* 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 rised 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 mutltiplatform fashon. 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( struct sigaction ) );
#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. */
/*sigfillset( &act.sa_mask );*/
#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 )
{
int pid;
/* Iconic? */
if( hb_parl( 1 ) )
{
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_threadEnterCriticalSection( &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_threadEnterCriticalSection( &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 dependant.
* TODO: Make it international through the xHarbour standard message system.
*/
HB_FUNC( HB_SIGNALDESC )
{
int iSig = hb_parni( 1 );
int iSubSig = hb_parni( 2 );
/* UNIX MESSGES */
#if defined( HB_OS_UNIX ) || defined( HB_OS_OS2_GCC )
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" );
case SIGUSR1:
hb_retc_const( "User defined" );
return;
case SIGUSR2:
hb_retc_const( "User defined (secondary)" );
return;
}
#endif
#ifdef HB_OS_WIN
if( iSig == 0 ) /* exception */
{
switch( iSubSig )
{
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