/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/




/* system headers */
#include "system.h"

#define MAX_STACK_FRAMES 256

#if defined( MACOSX )

#if defined( INTEL )
#include "backtrace.h"
#define INCLUDE_BACKTRACE
#define STACKTYPE "MacOsX_X86"
#endif /* INTEL */

#endif /* MACOSX */

#ifdef LINUX
#include <execinfo.h>
#include <link.h>
#define INCLUDE_BACKTRACE
#define STACKTYPE "Linux"
#endif

#ifdef SOLARIS

#include "backtrace.h"
#define INCLUDE_BACKTRACE

#if defined( SPARC )
#define STACKTYPE "Solaris_Sparc"
#elif defined( INTEL )
#define STACKTYPE "Solaris_X86"
#else
#define STACKTYPE "Solaris_Unknown"
#endif

#endif /* defined SOLARIS */

#include <osl/diagnose.h>
#include <osl/mutex.h>
#include <osl/signal.h>
#include <osl/process.h>
#include <osl/thread.h>
#include <rtl/bootstrap.h>
#include <rtl/digest.h>

#include "file_path_helper.h"

#define ACT_IGNORE	1
#define ACT_EXIT	2
#define ACT_SYSTEM	3
#define ACT_HIDE	4
#ifdef SAL_ENABLE_CRASH_REPORT
#    define ACT_ABORT	5
#else
#    define ACT_ABORT   ACT_SYSTEM
#endif

#define MAX_PATH_LEN	2048

typedef struct _oslSignalHandlerImpl
{
	oslSignalHandlerFunction      Handler;
	void*			        	  pData;
	struct _oslSignalHandlerImpl* pNext;
} oslSignalHandlerImpl;

static struct SignalAction
{
	int Signal;
	int Action;
    void (*Handler)(int);
} Signals[] =
{
	{ SIGHUP,    ACT_IGNORE, NULL },    /* hangup */
	{ SIGINT,    ACT_EXIT,   NULL },    /* interrupt (rubout) */
	{ SIGQUIT,   ACT_EXIT,  NULL },    /* quit (ASCII FS) */
	{ SIGILL,    ACT_SYSTEM,  NULL },    /* illegal instruction (not reset when caught) */
/* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
	{ SIGTRAP,   ACT_ABORT,  NULL },    /* trace trap (not reset when caught) */
#if ( SIGIOT != SIGABRT )
	{ SIGIOT,    ACT_ABORT,  NULL },    /* IOT instruction */
#endif 
	{ SIGABRT,   ACT_ABORT,  NULL },    /* used by abort, replace SIGIOT in the future */
#ifdef SIGEMT	
	{ SIGEMT,    ACT_SYSTEM,  NULL },    /* EMT instruction */
/* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/
/* SIGEMT may also be used by the profiler - so it is probably not a good
plan to have the new handler use this signal*/
#endif	
	{ SIGFPE,    ACT_ABORT,  NULL },    /* floating point exception */
	{ SIGKILL,   ACT_SYSTEM, NULL },    /* kill (cannot be caught or ignored) */
	{ SIGBUS,    ACT_ABORT,  NULL },    /* bus error */
	{ SIGSEGV,   ACT_ABORT,  NULL },    /* segmentation violation */
#ifdef SIGSYS
	{ SIGSYS,    ACT_ABORT,  NULL },    /* bad argument to system call */
#endif
	{ SIGPIPE,   ACT_HIDE,   NULL },    /* write on a pipe with no one to read it */
	{ SIGALRM,   ACT_EXIT,   NULL },    /* alarm clock */
	{ SIGTERM,   ACT_EXIT,   NULL },    /* software termination signal from kill */
	{ SIGUSR1,   ACT_SYSTEM, NULL },    /* user defined signal 1 */
	{ SIGUSR2,   ACT_SYSTEM, NULL },    /* user defined signal 2 */
	{ SIGCHLD,   ACT_SYSTEM, NULL },    /* child status change */
#ifdef SIGPWR
	{ SIGPWR,    ACT_IGNORE, NULL },    /* power-fail restart */
#endif
	{ SIGWINCH,  ACT_IGNORE, NULL },    /* window size change */
	{ SIGURG,    ACT_EXIT,   NULL },    /* urgent socket condition */
#ifdef SIGPOLL
	{ SIGPOLL,   ACT_EXIT,   NULL },    /* pollable event occured */
#endif
	{ SIGSTOP,   ACT_SYSTEM, NULL },    /* stop (cannot be caught or ignored) */
	{ SIGTSTP,   ACT_SYSTEM, NULL },    /* user stop requested from tty */
	{ SIGCONT,   ACT_SYSTEM, NULL },    /* stopped process has been continued */
	{ SIGTTIN,   ACT_SYSTEM, NULL },    /* background tty read attempted */
	{ SIGTTOU,   ACT_SYSTEM, NULL },    /* background tty write attempted */
	{ SIGVTALRM, ACT_EXIT,   NULL },    /* virtual timer expired */
	{ SIGPROF,   ACT_SYSTEM,   NULL },    /* profiling timer expired */
/*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do
not get taken by the new handler - the new handler does not pass on context 
information which causes 'collect' to crash. This is a way of avoiding
what looks like a bug in the new handler*/
    { SIGXCPU,   ACT_ABORT,  NULL },    /* exceeded cpu limit */
	{ SIGXFSZ,   ACT_ABORT,  NULL }     /* exceeded file size limit */
};
const int NoSignals = sizeof(Signals) / sizeof(struct SignalAction);

static sal_Bool               bErrorReportingEnabled = sal_True;
static sal_Bool  			  bInitSignal = sal_False;
static oslMutex 			  SignalListMutex;
static oslSignalHandlerImpl*  SignalList;
static sal_Bool				  bDoHardKill = sal_False;
static sal_Bool				  bSetSEGVHandler = sal_False;
static sal_Bool				  bSetWINCHHandler = sal_False;
static sal_Bool               bSetILLHandler = sal_False;

static void SignalHandlerFunction(int);

static void getExecutableName_Impl (rtl_String ** ppstrProgName)
{
	rtl_uString * ustrProgFile = 0;
	osl_getExecutableFile (&ustrProgFile);
	if (ustrProgFile)
	{
		rtl_uString * ustrProgName = 0;
		osl_systemPathGetFileNameOrLastDirectoryPart (ustrProgFile, &ustrProgName);
		if (ustrProgName != 0)
		{
			rtl_uString2String (
				ppstrProgName,
				rtl_uString_getStr (ustrProgName), rtl_uString_getLength (ustrProgName),
				osl_getThreadTextEncoding(),
				OUSTRING_TO_OSTRING_CVTFLAGS);
			rtl_uString_release (ustrProgName);
		}
		rtl_uString_release (ustrProgFile);
	}
}

static sal_Bool is_soffice_Impl (void)
{
	sal_Int32    idx       = -1;
	rtl_String * strProgName = 0;

	getExecutableName_Impl (&strProgName);
	if (strProgName)
	{
		idx = rtl_str_indexOfStr (rtl_string_getStr (strProgName), "soffice");
		rtl_string_release (strProgName);
	}
	return (idx != -1);
}

static sal_Bool InitSignal()
{
	int i;
	struct sigaction act;
	struct sigaction oact;
    sigset_t unset;

	if (is_soffice_Impl())
	{
		sal_uInt32	argi;
		sal_uInt32	argc;
		rtl_uString *ustrCommandArg = 0;

		argc = osl_getCommandArgCount();
		for ( argi = 0; argi < argc; argi++ )
		{
			if (osl_Process_E_None == osl_getCommandArg (argi, &ustrCommandArg))
			{
				if (0 == rtl_ustr_ascii_compare (rtl_uString_getStr (ustrCommandArg), "-bean"))
				{
					bDoHardKill = sal_True;
					break;
				}
			}
		}
		if (ustrCommandArg)
		{
			rtl_uString_release (ustrCommandArg);
			ustrCommandArg = 0;
		}

		// WORKAROUND FOR SEGV HANDLER CONFLICT
		//
		// the java jit needs SIGSEGV for proper work 
        // and we need SIGSEGV for the office crashguard
		//
		// TEMPORARY SOLUTION: 
		//   the office sets the signal handler during startup
		//   java can than overwrite it, if needed
		bSetSEGVHandler = sal_True;

		// WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
		bSetWINCHHandler = sal_True;

		// WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
		bSetILLHandler = sal_True;
	}

	SignalListMutex = osl_createMutex();

	act.sa_handler = SignalHandlerFunction;
	act.sa_flags   = SA_RESTART;

	sigfillset(&(act.sa_mask));

	/* Initialize the rest of the signals */
	for (i = 0; i < NoSignals; i++) 
	{
		/* hack: stomcatd is attaching JavaVM wich dont work with an sigaction(SEGV) */
		if ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV)
        && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH)
		&& (bSetILLHandler   || Signals[i].Signal != SIGILL))
		{
			if (Signals[i].Action != ACT_SYSTEM)
			{
				if (Signals[i].Action == ACT_HIDE)
				{
					struct sigaction ign;

					ign.sa_handler = SIG_IGN;
					ign.sa_flags   = 0;
                    sigemptyset(&ign.sa_mask);

					if (sigaction(Signals[i].Signal, &ign, &oact) == 0)
						Signals[i].Handler = oact.sa_handler;
					else
						Signals[i].Handler = SIG_DFL;
				}
				else
					if (sigaction(Signals[i].Signal, &act, &oact) == 0)
						Signals[i].Handler = oact.sa_handler;
					else
						Signals[i].Handler = SIG_DFL;
			}
		}
	}

    /* Clear signal mask inherited from parent process (on Mac OS X, upon a
       crash soffice re-execs itself from within the signal handler, so the
       second soffice would have the guilty signal blocked and would freeze upon
       encountering a similar crash again): */
    if (sigemptyset(&unset) < 0 ||
        pthread_sigmask(SIG_SETMASK, &unset, NULL) < 0)
    {
        OSL_TRACE("sigemptyset or pthread_sigmask failed");
    }

	return sal_True;
}

static sal_Bool DeInitSignal()
{
	int i;
	struct sigaction act;

	act.sa_flags   = 0;
	sigemptyset(&(act.sa_mask));

	/* Initialize the rest of the signals */
	for (i = NoSignals - 1; i >= 0; i--) 
		if (Signals[i].Action != ACT_SYSTEM)
		{
			act.sa_handler = Signals[i].Handler;

			sigaction(Signals[i].Signal, &act, NULL);
		}

	osl_destroyMutex(SignalListMutex);

	return sal_False;
}

#if defined (SAL_ENABLE_CRASH_REPORT) && defined(INCLUDE_BACKTRACE)

/*****************************************************************************/
/* Generate MD5 checksum	*/
/*****************************************************************************/

static sal_uInt32 calc_md5_checksum( const char *filename, sal_uInt8 *pChecksum, sal_uInt32 nChecksumLen )
{
	sal_uInt32	nBytesProcessed = 0;

	FILE *fp = fopen( filename, "r" );

	if ( fp )
	{
		rtlDigest digest = rtl_digest_createMD5();

		if ( digest )
		{
			size_t			nBytesRead;
			sal_uInt8		buffer[4096];
			rtlDigestError	error = rtl_Digest_E_None;

			while ( rtl_Digest_E_None == error &&
				0 != (nBytesRead = fread( buffer, 1, sizeof(buffer), fp )) )
			{
				error = rtl_digest_updateMD5( digest, buffer, nBytesRead );	
				nBytesProcessed += nBytesRead;
			}

			if ( rtl_Digest_E_None == error )
			{
				error = rtl_digest_getMD5( digest, pChecksum, nChecksumLen );
			}

			if ( rtl_Digest_E_None != error )
				nBytesProcessed = 0;

			rtl_digest_destroyMD5( digest );
		}

		fclose( fp );
	}

	return nBytesProcessed;
}

/*****************************************************************************/
/* Call crash reporter	*/
/*****************************************************************************/

/* Helper function to encode and write a string to a stream */

static int fputs_xml( const char *string, FILE *stream )
{
	int result = 0;

	while ( result >= 0 && *string )
	{
		switch( *string )
		{
		case '&':
			result = fputs( "&amp;", stream );
			break;
		case '<':
			result = fputs( "&lt;", stream );
			break;
		case '>':
			result = fputs( "&gt;", stream );
			break;
		default:
			result = fputc( *string, stream );
			break;
		}

		string++;
	}

	return result;
}
#endif

/* Create intermediate files and run crash reporter */

#define REPORTENV_PARAM		"-crashreportenv:"

#if defined SAL_ENABLE_CRASH_REPORT && defined INCLUDE_BACKTRACE && \
    defined LINUX

typedef struct
{
    const char *name;
    ElfW(Off) offset;
} dynamic_entry;

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
    const ElfW(Phdr) *pDynamic = NULL;

    if (size == sizeof(struct dl_phdr_info))
    {
        int i;
        for (i = 0; i < info->dlpi_phnum; ++i)
        {
            if (info->dlpi_phdr[i].p_type == PT_DYNAMIC)
            {
                pDynamic = &(info->dlpi_phdr[i]);
                break;
            }
        }
    }

    if (pDynamic)
    {
        char buffer[100];
        int len;
        char exe[PATH_MAX];
        const char *dsoname = info->dlpi_name;

        dynamic_entry* entry = (dynamic_entry*)data;

        if (strcmp(dsoname, "") == 0)
        { 
            snprintf(buffer, sizeof(buffer), "/proc/%d/exe", getpid());
            if ((len = readlink(buffer, exe, PATH_MAX)) != -1)
            {
                exe[len] = '\0';
                dsoname = exe;
            }
        }

        if (strcmp(dsoname, entry->name) == 0)
        {
            entry->offset = pDynamic->p_offset;
            return 1;
        }
    }
    return 0;
}

/* Get the location of the .dynamic section offset for the given elf file.
 * i.e. same as the "Offset" value shown for DYNAMIC from readelf -l foo 
 *
 * We want to know this value so that if the binaries have been modifed
 * by prelink then we can still process the call stack on server side 
 * by comparing this value to that of an "un-prelinked but known to be
 * otherwise equivalent" version of those binaries and adjust the call 
 * stack addresses by the differences between .dynamic addresses so as 
 * to be able to map the prelinked addresses back to the unprelinked 
 * addresses
 *
 * cmc@openoffice.org
 */
static ElfW(Off)
dynamic_section_offset(const char *name)
{
    dynamic_entry entry;

    entry.name = name;
    entry.offset = 0;

    dl_iterate_phdr(callback, &entry);

    return entry.offset;
}
#endif

static int ReportCrash( int Signal )
{
#ifdef SAL_ENABLE_CRASH_REPORT
	static sal_Bool bCrashReporterExecuted = sal_False;
	sal_Bool		bAutoCrashReport = sal_False;

	sal_uInt32	argi;
	sal_uInt32	argc;
	rtl_uString *ustrCommandArg = NULL;
	
	if ( !bErrorReportingEnabled )
		return -1;

	argc = osl_getCommandArgCount();
	
	for ( argi = 0; argi < argc; argi++ )
	{
		if ( osl_Process_E_None == osl_getCommandArg( argi, &ustrCommandArg ) )
		{
			if ( 0 == rtl_ustr_ascii_compare( rtl_uString_getStr( ustrCommandArg ), "-nocrashreport" ) )
			{
				rtl_uString_release( ustrCommandArg );
				return -1;
			}
			else if ( 0 == rtl_ustr_ascii_compare( rtl_uString_getStr( ustrCommandArg ), "-autocrashreport" ) )
			{
				bAutoCrashReport = sal_True;
			}
			else if ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( 
				rtl_uString_getStr( ustrCommandArg ), rtl_uString_getLength( ustrCommandArg ), 
				REPORTENV_PARAM, strlen(REPORTENV_PARAM) ) 
				)
			{
				rtl_uString	*ustrEnvironment = NULL;
				rtl_String *strEnv = NULL;

				rtl_uString_newFromStr( &ustrEnvironment, rtl_uString_getStr( ustrCommandArg ) + strlen(REPORTENV_PARAM) );

				if ( ustrEnvironment )
				{
					rtl_uString2String( 
						&strEnv, 
						rtl_uString_getStr( ustrEnvironment ), rtl_uString_getLength( ustrEnvironment ), 
						osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS 
						); 

					if ( strEnv )
					{
						putenv( rtl_string_getStr( strEnv ) );
						rtl_string_release( strEnv );
					}

					rtl_uString_release( ustrEnvironment );
				}

			}

		}
	}
	
	if ( ustrCommandArg )
		rtl_uString_release( ustrCommandArg );
	
	if ( !bCrashReporterExecuted )
	{
		int i;
		/* struct sigaction act; */

		for (i = 0; i < NoSignals; i++) 
		{
			if (Signals[i].Signal == Signal && Signals[i].Action == ACT_ABORT )
			{
				int  ret;
				char szShellCmd[512] = { '\0' };
				char *pXMLTempName = NULL;
				char *pStackTempName = NULL;
				char *pChecksumTempName = NULL;

#ifdef INCLUDE_BACKTRACE
				char szXMLTempNameBuffer[L_tmpnam];
				char szChecksumTempNameBuffer[L_tmpnam];
				char szStackTempNameBuffer[L_tmpnam];

				void *stackframes[MAX_STACK_FRAMES];
				int  iFrame;
				int  nFrames = backtrace( stackframes, sizeof(stackframes)/sizeof(stackframes[0]));

				FILE *xmlout = NULL, *stackout = NULL, *checksumout = NULL;
				int fdxml, fdstk, fdchksum;

				strncpy( szXMLTempNameBuffer, P_tmpdir, sizeof(szXMLTempNameBuffer) );
				strncat( szXMLTempNameBuffer, "/crxmlXXXXXX", sizeof(szXMLTempNameBuffer) );

				strncpy( szStackTempNameBuffer, P_tmpdir, sizeof(szStackTempNameBuffer) );
				strncat( szStackTempNameBuffer, "/crstkXXXXXX", sizeof(szStackTempNameBuffer) );

				strncpy( szChecksumTempNameBuffer, P_tmpdir, sizeof(szChecksumTempNameBuffer) );
				strncat( szChecksumTempNameBuffer, "/crchkXXXXXX", sizeof(szChecksumTempNameBuffer) );

				fdxml = mkstemp(szXMLTempNameBuffer);
				fdstk = mkstemp(szStackTempNameBuffer);
				fdchksum = mkstemp(szChecksumTempNameBuffer);

				xmlout = fdopen( fdxml , "w" );
				stackout = fdopen( fdstk , "w" );
				checksumout = fdopen( fdchksum, "w" );
			
                pXMLTempName = szXMLTempNameBuffer;
                pStackTempName = szStackTempNameBuffer;
				pChecksumTempName = szChecksumTempNameBuffer;


				if ( xmlout && stackout && checksumout )
				{
					fprintf( xmlout, "<errormail:Stack type=\"%s\">\n", STACKTYPE );

					fprintf( checksumout, "<errormail:Checksums type=\"MD5\">\n" );

					for ( iFrame = 0; iFrame < nFrames; iFrame++ )
					{
						Dl_info dl_info;

						fprintf( stackout, "0x%" SAL_PRIxUINTPTR ":",
							SAL_INT_CAST(sal_uIntPtr, stackframes[iFrame]) );

						fprintf( xmlout, "<errormail:StackInfo pos=\"%d\" ip=\"0x%" SAL_PRIxUINTPTR "\"",
							iFrame,
							SAL_INT_CAST(sal_uIntPtr, stackframes[iFrame])
							);

						memset( &dl_info, 0, sizeof(dl_info) );

						/* dladdr may fail */
						if ( dladdr( stackframes[iFrame], &dl_info) )
						{
							const char *dli_fname = NULL;
							char *dli_fdir = NULL;
							char szDirectory[PATH_MAX];
							char szCanonicDirectory[PATH_MAX];

							/* Don't expect that dladdr filled all members of dl_info */

							dli_fname = dl_info.dli_fname ? strrchr(  dl_info.dli_fname, '/' ) : NULL;
							if ( dli_fname )
							{
								++dli_fname;
								memcpy( szDirectory, dl_info.dli_fname, dli_fname - dl_info.dli_fname );
								szDirectory[dli_fname - dl_info.dli_fname] = 0;

								dli_fdir = realpath( szDirectory, szCanonicDirectory ) ? szCanonicDirectory : szDirectory;

								if ( *dli_fdir && dli_fdir[ strlen(dli_fdir) - 1 ] != '/' )
									strcat( dli_fdir, "/" );
							}
							else
								dli_fname = dl_info.dli_fname;

							/* create checksum of library on stack */
							if ( dli_fname )
							{
								sal_uInt8	checksum[RTL_DIGEST_LENGTH_MD5];

								sal_uInt32 nBytesProcessed = calc_md5_checksum( 
									dl_info.dli_fname, checksum, sizeof(checksum) );
								if ( nBytesProcessed )
								{
									int j;

									fprintf( checksumout, "<errormail:Checksum sum=\"0x" );
									for ( j = 0; j < 16; fprintf( checksumout, "%02X", checksum[j++] ) );
									fprintf( checksumout,
                                        "\" bytes=\"%lu\" file=\"%s\"/>\n",
                                        SAL_INT_CAST(
                                            unsigned long, nBytesProcessed),
										dli_fname );
								}
							}

							if ( dl_info.dli_fbase && dl_info.dli_fname )
							{
#ifdef LINUX
								ElfW(Off) dynamic_offset = dynamic_section_offset(dl_info.dli_fname);
								fprintf( stackout, " 0x%" SAL_PRI_SIZET "x:", dynamic_offset);
#endif

								fprintf( stackout, " %s + 0x%" SAL_PRI_PTRDIFFT "x",
									dl_info.dli_fname,
									(char*)stackframes[iFrame] - (char*)dl_info.dli_fbase
									);

								fprintf( xmlout, " rel=\"0x%" SAL_PRI_PTRDIFFT "x\"", (char *)stackframes[iFrame] - (char *)dl_info.dli_fbase );
								if ( dli_fname )
									fprintf( xmlout, " name=\"%s\"", dli_fname );

								if ( dli_fdir )
									fprintf( xmlout, " path=\"%s\"", dli_fdir );

#ifdef LINUX
								fprintf( xmlout, " dynamicoffset=\"0x%" SAL_PRI_SIZET "x\"", dynamic_offset );
#endif
							}
							else
								fprintf( stackout, " ????????" );

							if ( dl_info.dli_sname && dl_info.dli_saddr )
							{
								fputs( " (", stackout );
								fputs_xml( dl_info.dli_sname, stackout );
								fprintf( stackout, " + 0x%" SAL_PRI_PTRDIFFT "x)", 
									(char*)stackframes[iFrame] - (char*)dl_info.dli_saddr );

								fputs( " ordinal=\"", xmlout );
								fputs_xml( dl_info.dli_sname, xmlout );
								fprintf( xmlout, "+0x%" SAL_PRI_PTRDIFFT "x\"", 
									(char *)stackframes[iFrame] - (char *)dl_info.dli_saddr );
							}

						}
						else /* dladdr failed */
						{
							fprintf( stackout, " ????????" );
						}

						fprintf( stackout, "\n" );
						fprintf( xmlout, "/>\n" );

					}

					fprintf( xmlout, "</errormail:Stack>\n" );
					fprintf( checksumout, "</errormail:Checksums>\n" );
				}
				else
				{
				    pXMLTempName = NULL;
				    pStackTempName = NULL;
					pChecksumTempName = NULL;
				}
				
				if ( stackout )
					fclose( stackout );
				if ( xmlout )
					fclose( xmlout );
				if ( checksumout )
					fclose( checksumout );

                if ( pXMLTempName && pChecksumTempName && pStackTempName )
#endif /* INCLUDE_BACKTRACE */
                {
                    rtl_uString * crashrep_url = NULL;
                    rtl_uString * crashrep_path = NULL;
                    rtl_String  * crashrep_path_system = NULL;
                    rtl_string2UString(
                        &crashrep_url,
                        RTL_CONSTASCII_USTRINGPARAM(
                            "$OOO_BASE_DIR/program/crashrep"),
                        OSTRING_TO_OUSTRING_CVTFLAGS);
                    rtl_bootstrap_expandMacros(&crashrep_url);
                    osl_getSystemPathFromFileURL(crashrep_url, &crashrep_path);
                    rtl_uString2String(
                        &crashrep_path_system,
                        rtl_uString_getStr(crashrep_path),
                        rtl_uString_getLength(crashrep_path),
                        osl_getThreadTextEncoding(),
                        (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
                         | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR));
                    rtl_uString_release(crashrep_url);
                    rtl_uString_release(crashrep_path);
#if defined INCLUDE_BACKTRACE && (defined LINUX || defined MACOSX)
					snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]),
                        "%s -p %d -s %d -xml %s -chksum %s -stack %s -noui%s",
                        rtl_string_getStr(crashrep_path_system),
						getpid(), 
						Signal, 
						pXMLTempName, 
						pChecksumTempName, 
						pStackTempName,
                        bAutoCrashReport ? " -send" : "" );
#elif defined INCLUDE_BACKTRACE && defined SOLARIS
					snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]),
                        "%s -p %d -s %d -xml %s -chksum %s -noui%s",
                        rtl_string_getStr(crashrep_path_system),
						getpid(), 
						Signal, 
						pXMLTempName, 
						pChecksumTempName,
                        bAutoCrashReport ? " -send" : "" );
#else
                    snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]),
                        "%s -p %d -s %d -noui%s",
                        rtl_string_getStr(crashrep_path_system),
                        getpid(), Signal, bAutoCrashReport ? " -send" : "" );
#endif
                    rtl_string_release(crashrep_path_system);
                }

                ret = szShellCmd[0] == '\0' ? -1 : system( szShellCmd );

				if ( pXMLTempName )
					unlink( pXMLTempName );

				if ( pStackTempName )
					unlink( pStackTempName );

				if ( pChecksumTempName )
					unlink( pChecksumTempName );
					
				if ( -1 != ret )
				{
					bCrashReporterExecuted = sal_True;
					return 1;
				}
				else
					return -1;
				
			}
		}
		
		return 0;
	}
	
	return 1;
#else /* defined SAL_ENABLE_CRASH_REPORT */
    /* the utility crash_report is not build, so do the same as when
       the option -nocrashreport is used */
    (void) Signal; // avoid warnings
    return -1;
#endif /* defined SAL_ENABLE_CRASH_REPORT */
}

static void PrintStack( int sig )
{
#if ! defined(MACOSX) || defined(INCLUDE_BACKTRACE)
	void *buffer[MAX_STACK_FRAMES];
	int size = backtrace( buffer, sizeof(buffer) / sizeof(buffer[0]) );
#endif

	fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig );

#if defined(MACOSX) && ! defined(INCLUDE_BACKTRACE)
	fprintf( stderr, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" );
#else
	if ( size > 0 )
	{
		fputs( "Stack:\n", stderr );
		backtrace_symbols_fd( buffer, size, fileno(stderr) );
	}
#endif
}

static oslSignalAction CallSignalHandler(oslSignalInfo *pInfo)
{
	oslSignalHandlerImpl* pHandler = SignalList;
	oslSignalAction Action = osl_Signal_ActCallNextHdl;

	while (pHandler != NULL)
	{
		if ((Action = pHandler->Handler(pHandler->pData, pInfo)) 
		    != osl_Signal_ActCallNextHdl)
			break;

		pHandler = pHandler->pNext;
	}
    
	return Action;
}

void CallSystemHandler(int Signal)
{
	int i;
	struct sigaction act;

	for (i = 0; i < NoSignals; i++) 
	{
		if (Signals[i].Signal == Signal)
			break;
	}

	if (i < NoSignals)
	{
		if ((Signals[i].Handler == NULL)    ||
			(Signals[i].Handler == SIG_DFL) ||
			(Signals[i].Handler == SIG_IGN) ||
	     	(Signals[i].Handler == SIG_ERR))
		{
			switch (Signals[i].Action)
			{
				case ACT_EXIT:		/* terminate */
                    /* prevent dumping core on exit() */
					_exit(255);
					break;

				case ACT_ABORT:		/* terminate witch core dump */
					ReportCrash( Signal );
					act.sa_handler = SIG_DFL;
					act.sa_flags   = 0;
					sigemptyset(&(act.sa_mask));
					sigaction(SIGABRT, &act, NULL);
					PrintStack( Signal );
					abort();
					break;

				case ACT_IGNORE:	/* ignore */
					break;

				default:			/* should never happen */
					OSL_ASSERT(0);
			}
		}
		else
			(*Signals[i].Handler)(Signal);
	}
}


/*****************************************************************************/
/* SignalHandlerFunction	*/
/*****************************************************************************/
void SignalHandlerFunction(int Signal)
{
	oslSignalInfo	 Info;
	struct sigaction act;

	Info.UserSignal = Signal;
	Info.UserData   = NULL;

	switch (Signal)
	{
		case SIGBUS:
		case SIGILL:
		case SIGSEGV:
		case SIGIOT:
#if ( SIGIOT != SIGABRT )
	    case SIGABRT:
#endif
			Info.Signal = osl_Signal_AccessViolation;
			break; 

		case -1:
			Info.Signal = osl_Signal_IntegerDivideByZero;
			break; 

		case SIGFPE:
			Info.Signal = osl_Signal_FloatDivideByZero;
			break; 

		case SIGINT:
		case SIGTERM:
    case SIGQUIT:
    case SIGHUP:
			Info.Signal = osl_Signal_Terminate;
			break; 

		default:
			Info.Signal = osl_Signal_System;
			break; 
	}

	ReportCrash( Signal );
	
	/* Portal Demo HACK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
	if (bDoHardKill && (Info.Signal == osl_Signal_AccessViolation))
		_exit(255);
	/* Portal Demo HACK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    

	switch (CallSignalHandler(&Info))
	{
	case osl_Signal_ActCallNextHdl:
		CallSystemHandler(Signal);
		break;

	case osl_Signal_ActAbortApp:
		ReportCrash( Signal );
		act.sa_handler = SIG_DFL;
		act.sa_flags   = 0;
		sigemptyset(&(act.sa_mask));
		sigaction(SIGABRT, &act, NULL);
		PrintStack( Signal );
		abort();
		break;

	case osl_Signal_ActKillApp:
        /* prevent dumping core on exit() */
		_exit(255);
		break;
	default:
		break;
	}
}

/*****************************************************************************/
/* osl_addSignalHandler */
/*****************************************************************************/
oslSignalHandler SAL_CALL osl_addSignalHandler(oslSignalHandlerFunction Handler, void* pData)
{
	oslSignalHandlerImpl* pHandler;

	OSL_ASSERT(Handler != NULL);
	if ( Handler == 0 )
	{
		return 0;
	}

	if (! bInitSignal)
		bInitSignal = InitSignal();

	pHandler = (oslSignalHandlerImpl*) calloc(1, sizeof(oslSignalHandlerImpl));

	if (pHandler != NULL)
	{
		pHandler->Handler = Handler;
		pHandler->pData   = pData;

		osl_acquireMutex(SignalListMutex);
	
		pHandler->pNext = SignalList;
		SignalList      = pHandler;

		osl_releaseMutex(SignalListMutex);

		return (pHandler);
	}
	
	return (NULL);
}

/*****************************************************************************/
/* osl_removeSignalHandler */
/*****************************************************************************/
sal_Bool SAL_CALL osl_removeSignalHandler(oslSignalHandler Handler)
{
	oslSignalHandlerImpl *pHandler, *pPrevious = NULL;

	OSL_ASSERT(Handler != NULL);

	if (! bInitSignal)
		bInitSignal = InitSignal();

	osl_acquireMutex(SignalListMutex);

	pHandler = SignalList;

	while (pHandler != NULL)
	{
		if (pHandler == Handler)
		{
			if (pPrevious)
				pPrevious->pNext = pHandler->pNext;
			else
				SignalList = pHandler->pNext;

			osl_releaseMutex(SignalListMutex);

			if (SignalList == NULL)
				bInitSignal = DeInitSignal();

			free(pHandler);

			return (sal_True);
		}

		pPrevious = pHandler;
		pHandler  = pHandler->pNext;
	}

	osl_releaseMutex(SignalListMutex);

	return (sal_False);
}

/*****************************************************************************/
/* osl_raiseSignal */
/*****************************************************************************/
oslSignalAction SAL_CALL osl_raiseSignal(sal_Int32 UserSignal, void* UserData)
{
	oslSignalInfo   Info;
	oslSignalAction Action;

	if (! bInitSignal)
		bInitSignal = InitSignal();

	osl_acquireMutex(SignalListMutex);

	Info.Signal     = osl_Signal_User;
	Info.UserSignal = UserSignal;
	Info.UserData   = UserData;

	Action = CallSignalHandler(&Info);

	osl_releaseMutex(SignalListMutex);

	return (Action);
}

/*****************************************************************************/
/* osl_setErrorReporting */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setErrorReporting( sal_Bool bEnable )
{
	sal_Bool bOld = bErrorReportingEnabled;
	bErrorReportingEnabled = bEnable;

	return bOld;
}
