/*
 * This file is part of the KDE Libraries
 * Copyright (C) 2000 Timo Hummel <timo.hummel@sap.com>
 *                    Tom Braun <braunt@fh-konstanz.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "akcrash.h"
#include "akdebug.h"

#include <QtCore/QString>

#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

#include <config-akonadi.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_EXECINFO_H
# include <execinfo.h>
#endif

QString akBacktrace()
{
  QString s;

/* FIXME: is there an equivalent for darwin, *BSD, or windows? */
#ifdef HAVE_EXECINFO_H
  void* trace[256];
  int n = backtrace(trace, 256);
  if (!n)
    return s;

  char** strings = backtrace_symbols (trace, n);

  s = QLatin1String("[\n");

  for ( int i = 0; i < n; ++i ) {
    s += QString::number(i) +
         QLatin1String(": ") +
         QLatin1String(strings[i]) + QLatin1String("\n");
  }
  s += QLatin1String("]\n");

  if (strings)
    free (strings);
#endif

  return s;
}

static AkonadiCrash::HandlerType s_emergencyMethod = 0;
static AkonadiCrash::HandlerType s_shutdownMethod = 0;
static int recursionCount = 0;

void AkonadiCrash::setEmergencyMethod( HandlerType method )
{
  s_emergencyMethod = method;
}

void AkonadiCrash::setShutdownMethod( HandlerType method )
{
  s_shutdownMethod = method;
}

static void defaultCrashHandler( int sig )
{
  ++recursionCount;
  if ( recursionCount <= 2 ) {
    if ( sig != SIGTERM && sig != SIGINT ) {
      if ( recursionCount == 1 )
        akError() << akBacktrace();
      else // fall back to something more simple in case the other one crashed itself
        fprintf( stderr, "%s", akBacktrace().toLatin1().data() );

      if ( s_emergencyMethod )
        s_emergencyMethod( sig );
    } else {
      if ( s_shutdownMethod )
        s_shutdownMethod( sig );
    }
  }

#ifdef Q_CC_MINGW
  _Exit(255);
#else
  _exit(255);
#endif
}

void AkonadiCrash::init()
{
  HandlerType handler = defaultCrashHandler;

#ifdef Q_OS_UNIX
  if (!handler)
    handler = SIG_DFL;

  sigset_t mask;
  sigemptyset(&mask);

#ifdef SIGSEGV
  signal (SIGSEGV, handler);
  sigaddset(&mask, SIGSEGV);
#endif
#ifdef SIGFPE
  signal (SIGFPE, handler);
  sigaddset(&mask, SIGFPE);
#endif
#ifdef SIGILL
  signal (SIGILL, handler);
  sigaddset(&mask, SIGILL);
#endif
#ifdef SIGABRT
  signal (SIGABRT, handler);
  sigaddset(&mask, SIGABRT);
#endif
#ifdef SIGTERM
  signal (SIGTERM, handler);
  sigaddset(&mask, SIGTERM);
#endif
#ifdef SIGINT
  signal (SIGINT, handler);
  sigaddset(&mask, SIGINT);
#endif

  sigprocmask(SIG_UNBLOCK, &mask, 0);
#endif //Q_OS_UNIX

}

