summaryrefslogtreecommitdiffstats
path: root/sal/osl/unx/signal.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sal/osl/unx/signal.cxx')
-rw-r--r--sal/osl/unx/signal.cxx478
1 files changed, 478 insertions, 0 deletions
diff --git a/sal/osl/unx/signal.cxx b/sal/osl/unx/signal.cxx
new file mode 100644
index 000000000..79721def6
--- /dev/null
+++ b/sal/osl/unx/signal.cxx
@@ -0,0 +1,478 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <signalshared.hxx>
+
+#include <config_features.h>
+
+#include "soffice.hxx"
+/* system headers */
+#include "system.hxx"
+
+#if defined( MACOSX )
+
+#if defined( INTEL )
+#include "backtrace.h"
+#define INCLUDE_BACKTRACE
+#endif /* INTEL */
+
+#endif /* MACOSX */
+
+#ifdef LINUX
+#include <execinfo.h>
+#include <link.h>
+#define INCLUDE_BACKTRACE
+#endif
+
+#ifdef __sun
+
+#include "backtrace.h"
+#define INCLUDE_BACKTRACE
+
+#endif /* defined __sun */
+
+#if defined INCLUDE_BACKTRACE
+#define MAX_STACK_FRAMES 256
+#endif
+
+#include <osl/diagnose.h>
+#include <osl/signal.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <rtl/bootstrap.h>
+#include <rtl/digest.h>
+
+#include "file_path_helper.hxx"
+#define ACT_IGNORE 1
+#define ACT_EXIT 2
+#define ACT_SYSTEM 3
+#define ACT_HIDE 4
+#define ACT_ABORT 5
+
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/memcheck.h>
+#endif
+
+namespace
+{
+extern "C" using Handler1 = void (*)(int);
+extern "C" using Handler2 = void (*)(int, siginfo_t *, void *);
+struct SignalAction
+{
+ int Signal;
+ int Action;
+ Handler1 Handler;
+ bool siginfo; // Handler's type is Handler2
+} Signals[] =
+{
+ { SIGHUP, ACT_HIDE, SIG_DFL, false }, /* hangup */
+ { SIGINT, ACT_EXIT, SIG_DFL, false }, /* interrupt (rubout) */
+ { SIGQUIT, ACT_EXIT, SIG_DFL, false }, /* quit (ASCII FS) */
+ { SIGILL, ACT_SYSTEM, SIG_DFL, false }, /* illegal instruction (not reset when caught) */
+/* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
+ { SIGTRAP, ACT_ABORT, SIG_DFL, false }, /* trace trap (not reset when caught) */
+#if ( SIGIOT != SIGABRT )
+ { SIGIOT, ACT_ABORT, SIG_DFL, false }, /* IOT instruction */
+#endif
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGABRT, ACT_SYSTEM, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#else
+ { SIGABRT, ACT_ABORT, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#endif
+#ifdef SIGEMT
+ { SIGEMT, ACT_SYSTEM, SIG_DFL, false }, /* 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, SIG_DFL, false }, /* floating point exception */
+ { SIGKILL, ACT_SYSTEM, SIG_DFL, false }, /* kill (cannot be caught or ignored) */
+ { SIGBUS, ACT_ABORT, SIG_DFL, false }, /* bus error */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGSEGV, ACT_SYSTEM, SIG_DFL, false }, /* segmentation violation */
+#else
+ { SIGSEGV, ACT_ABORT, SIG_DFL, false }, /* segmentation violation */
+#endif
+#ifdef SIGSYS
+ { SIGSYS, ACT_ABORT, SIG_DFL, false }, /* bad argument to system call */
+#endif
+ { SIGPIPE, ACT_HIDE, SIG_DFL, false }, /* write on a pipe with no one to read it */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGALRM, ACT_SYSTEM, SIG_DFL, false }, /* alarm clock */
+#else
+ { SIGALRM, ACT_EXIT, SIG_DFL, false }, /* alarm clock */
+#endif
+ { SIGTERM, ACT_EXIT, SIG_DFL, false }, /* software termination signal from kill */
+ { SIGUSR1, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 1 */
+ { SIGUSR2, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 2 */
+ { SIGCHLD, ACT_SYSTEM, SIG_DFL, false }, /* child status change */
+#ifdef SIGPWR
+ { SIGPWR, ACT_IGNORE, SIG_DFL, false }, /* power-fail restart */
+#endif
+ { SIGWINCH, ACT_IGNORE, SIG_DFL, false }, /* window size change */
+ { SIGURG, ACT_EXIT, SIG_DFL, false }, /* urgent socket condition */
+#ifdef SIGPOLL
+ { SIGPOLL, ACT_EXIT, SIG_DFL, false }, /* pollable event occurred */
+#endif
+ { SIGSTOP, ACT_SYSTEM, SIG_DFL, false }, /* stop (cannot be caught or ignored) */
+ { SIGTSTP, ACT_SYSTEM, SIG_DFL, false }, /* user stop requested from tty */
+ { SIGCONT, ACT_SYSTEM, SIG_DFL, false }, /* stopped process has been continued */
+ { SIGTTIN, ACT_SYSTEM, SIG_DFL, false }, /* background tty read attempted */
+ { SIGTTOU, ACT_SYSTEM, SIG_DFL, false }, /* background tty write attempted */
+ { SIGVTALRM, ACT_EXIT, SIG_DFL, false }, /* virtual timer expired */
+ { SIGPROF, ACT_SYSTEM, SIG_DFL, false }, /* 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, SIG_DFL, false }, /* exceeded cpu limit */
+ { SIGXFSZ, ACT_ABORT, SIG_DFL, false } /* exceeded file size limit */
+};
+const int NoSignals = SAL_N_ELEMENTS(Signals);
+
+bool bSetSEGVHandler = false;
+bool bSetWINCHHandler = false;
+bool bSetILLHandler = false;
+
+void signalHandlerFunction(int, siginfo_t *, void *);
+
+#if HAVE_FEATURE_BREAKPAD
+bool is_unset_signal(int signal)
+{
+#ifdef DBG_UTIL
+ return (!bSetSEGVHandler && signal == SIGSEGV) ||
+ (!bSetWINCHHandler && signal == SIGWINCH) ||
+ (!bSetILLHandler && signal == SIGILL);
+#else
+ (void) signal;
+ return false;
+#endif
+}
+#endif
+
+}
+
+bool onInitSignal()
+{
+ if (sal::detail::isSoffice())
+ {
+ // 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 = true;
+
+ // WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
+ bSetWINCHHandler = true;
+
+ // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
+ bSetILLHandler = true;
+ }
+
+#ifdef DBG_UTIL
+ bSetSEGVHandler = bSetWINCHHandler = bSetILLHandler = false;
+#endif
+
+ struct sigaction act;
+ act.sa_sigaction = signalHandlerFunction;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ sigfillset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (SignalAction & rSignal : Signals)
+ {
+#if defined HAVE_VALGRIND_HEADERS
+ if (rSignal.Signal == SIGUSR2 && RUNNING_ON_VALGRIND)
+ rSignal.Action = ACT_IGNORE;
+#endif
+
+ /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */
+ if ((bSetSEGVHandler || rSignal.Signal != SIGSEGV)
+ && (bSetWINCHHandler || rSignal.Signal != SIGWINCH)
+ && (bSetILLHandler || rSignal.Signal != SIGILL))
+ {
+ if (rSignal.Action != ACT_SYSTEM)
+ {
+ if (rSignal.Action == ACT_HIDE)
+ {
+ struct sigaction ign;
+
+ ign.sa_handler = SIG_IGN;
+ ign.sa_flags = 0;
+ sigemptyset(&ign.sa_mask);
+
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &ign, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ else
+ {
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &act, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ }
+ }
+ }
+
+ /* Clear signal mask inherited from parent process (on macOS, 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): */
+ sigset_t unset;
+ if (sigemptyset(&unset) < 0 ||
+ pthread_sigmask(SIG_SETMASK, &unset, nullptr) < 0)
+ {
+ SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed");
+ }
+
+ return true;
+}
+
+bool onDeInitSignal()
+{
+ struct sigaction act;
+
+ sigemptyset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (int i = NoSignals - 1; i >= 0; i--)
+ if (Signals[i].Action != ACT_SYSTEM
+ && ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV)
+ && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH)
+ && (bSetILLHandler || Signals[i].Signal != SIGILL)))
+ {
+ if (Signals[i].siginfo) {
+ act.sa_sigaction = reinterpret_cast<Handler2>(
+ Signals[i].Handler);
+ act.sa_flags = SA_SIGINFO;
+ } else {
+ act.sa_handler = Signals[i].Handler;
+ act.sa_flags = 0;
+ }
+
+ sigaction(Signals[i].Signal, &act, nullptr);
+ }
+
+ return false;
+}
+
+namespace
+{
+void printStack(int sig)
+{
+#ifdef INCLUDE_BACKTRACE
+ void *buffer[MAX_STACK_FRAMES];
+ int size = backtrace( buffer, SAL_N_ELEMENTS(buffer) );
+#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
+#ifdef INCLUDE_BACKTRACE
+ if ( size > 0 )
+ {
+ fputs( "Stack:\n", stderr );
+ backtrace_symbols_fd( buffer, size, fileno(stderr) );
+ }
+#endif
+#endif
+}
+
+void callSystemHandler(int signal, siginfo_t * info, void * context)
+{
+ int i;
+
+ for (i = 0; i < NoSignals; i++)
+ {
+ if (Signals[i].Signal == signal)
+ break;
+ }
+
+ if (i >= NoSignals)
+ return;
+
+ if ((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 with core dump */
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case ACT_IGNORE: /* ignore */
+ break;
+
+ default: /* should never happen */
+ OSL_ASSERT(false);
+ }
+ }
+ else if (Signals[i].siginfo) {
+ (*reinterpret_cast<Handler2>(Signals[i].Handler))(
+ signal, info, context);
+ } else {
+ (*Signals[i].Handler)(signal);
+ }
+}
+
+#if defined HAVE_VALGRIND_HEADERS
+void DUMPCURRENTALLOCS()
+{
+ VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" );
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+ VALGRIND_DO_LEAK_CHECK;
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+
+ VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" );
+}
+#endif
+
+void signalHandlerFunction(int signal, siginfo_t * info, void * context)
+{
+ oslSignalInfo Info;
+
+ Info.UserSignal = signal;
+ Info.UserData = nullptr;
+
+ 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:
+ Info.Signal = osl_Signal_Terminate;
+ break;
+
+#if defined HAVE_VALGRIND_HEADERS
+ case SIGUSR2:
+ if (RUNNING_ON_VALGRIND)
+ DUMPCURRENTALLOCS();
+ Info.Signal = osl_Signal_System;
+ break;
+#endif
+
+ default:
+ Info.Signal = osl_Signal_System;
+ break;
+ }
+
+#if HAVE_FEATURE_BREAKPAD
+ if ((Info.Signal == osl_Signal_AccessViolation ||
+ Info.Signal == osl_Signal_IntegerDivideByZero ||
+ Info.Signal == osl_Signal_FloatDivideByZero) && !is_unset_signal(signal))
+ {
+ callSystemHandler(signal, info, context);
+ }
+#endif
+
+ switch (callSignalHandler(&Info))
+ {
+ case osl_Signal_ActCallNextHdl:
+ callSystemHandler(signal, info, context);
+ break;
+
+ case osl_Signal_ActAbortApp:
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case osl_Signal_ActKillApp:
+ /* prevent dumping core on exit() */
+ _exit(255);
+ break;
+ default:
+ break;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */