diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:56:40 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:56:40 -0600 |
commit | e16866e072f94410321d70daedbcb855ea878cac (patch) | |
tree | ee3f52eabde7da1a0e6ca845fb9c2813cf1558cf /tdecore/kcrash.cpp | |
parent | a58c20c1a7593631a1b50213c805507ebc16adaf (diff) | |
download | tdelibs-e16866e072f94410321d70daedbcb855ea878cac.tar.gz tdelibs-e16866e072f94410321d70daedbcb855ea878cac.zip |
Actually move the kde files that were renamed in the last commit
Diffstat (limited to 'tdecore/kcrash.cpp')
-rw-r--r-- | tdecore/kcrash.cpp | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/tdecore/kcrash.cpp b/tdecore/kcrash.cpp new file mode 100644 index 000000000..dbbd41338 --- /dev/null +++ b/tdecore/kcrash.cpp @@ -0,0 +1,524 @@ +/* + * 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. + */ + +/* + * This file is used to catch signals which would normally + * crash the application (like segmentation fault, floating + * point exception and such). + */ + +#include "config.h" + +#include <string.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include "kcrash.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/un.h> +#include <sys/socket.h> +#include <errno.h> + +#include <tqwindowdefs.h> +#include <kglobal.h> +#include <kinstance.h> +#include <kaboutdata.h> +#include <kdebug.h> +#include <kapplication.h> +#include <dcopclient.h> + +#include <../kinit/klauncher_cmds.h> + +#if defined Q_WS_X11 +#include <X11/Xlib.h> +#endif + +KCrash::HandlerType KCrash::_emergencySaveFunction = 0; +KCrash::HandlerType KCrash::_crashHandler = 0; +const char *KCrash::appName = 0; +const char *KCrash::appPath = 0; +bool KCrash::safer = false; + +// This function sets the function which should be called when the +// application crashes and the +// application is asked to try to save its data. +void +KCrash::setEmergencySaveFunction (HandlerType saveFunction) +{ + _emergencySaveFunction = saveFunction; + + /* + * We need at least the default crash handler for + * emergencySaveFunction to be called + */ + if (_emergencySaveFunction && !_crashHandler) + _crashHandler = defaultCrashHandler; +} + + +// This function sets the function which should be responsible for +// the application crash handling. +void +KCrash::setCrashHandler (HandlerType handler) +{ +#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 + + sigprocmask(SIG_UNBLOCK, &mask, 0); +#endif //Q_OS_UNIX + + _crashHandler = handler; +} + +void +KCrash::defaultCrashHandler (int sig) +{ +#ifdef Q_OS_UNIX + // WABA: Do NOT use kdDebug() in this function because it is much too risky! + // Handle possible recursions + static int crashRecursionCounter = 0; + crashRecursionCounter++; // Nothing before this, please ! + + signal(SIGALRM, SIG_DFL); + alarm(3); // Kill me... (in case we deadlock in malloc) + + if (crashRecursionCounter < 2) { + if (_emergencySaveFunction) { + _emergencySaveFunction (sig); + } + crashRecursionCounter++; // + } + + // Close all remaining file descriptors except for stdin/stdout/stderr + struct rlimit rlp; + getrlimit(RLIMIT_NOFILE, &rlp); + for (int i = 3; i < (int)rlp.rlim_cur; i++) + close(i); + + + // this code is leaking, but this should not hurt cause we will do a + // exec() afterwards. exec() is supposed to clean up. + if (crashRecursionCounter < 3) + { + if (appName) + { +#ifndef NDEBUG + fprintf(stderr, "KCrash: crashing... crashRecursionCounter = %d\n", crashRecursionCounter); + fprintf(stderr, "KCrash: Application Name = %s path = %s pid = %d\n", appName ? appName : "<unknown>" , appPath ? appPath : "<unknown>", getpid()); +#else + fprintf(stderr, "KCrash: Application '%s' crashing...\n", appName ? appName : "<unknown>"); +#endif + + const char * argv[24]; // don't forget to update this + int i = 0; + + // argument 0 has to be drkonqi + argv[i++] = "drkonqi"; + +#if defined Q_WS_X11 + // start up on the correct display + argv[i++] = "-display"; + if ( qt_xdisplay() ) + argv[i++] = XDisplayString(qt_xdisplay()); + else + argv[i++] = getenv("DISPLAY"); +#elif defined(Q_WS_QWS) + // start up on the correct display + argv[i++] = "-display"; + argv[i++] = getenv("QWS_DISPLAY"); +#endif + + // we have already tested this + argv[i++] = "--appname"; + argv[i++] = appName; + if (KApplication::loadedByKdeinit) + argv[i++] = "--tdeinit"; + + // only add apppath if it's not NULL + if (appPath) { + argv[i++] = "--apppath"; + argv[i++] = appPath; + } + + // signal number -- will never be NULL + char sigtxt[ 10 ]; + sprintf( sigtxt, "%d", sig ); + argv[i++] = "--signal"; + argv[i++] = sigtxt; + + char pidtxt[ 10 ]; + sprintf( pidtxt, "%d", getpid()); + argv[i++] = "--pid"; + argv[i++] = pidtxt; + + const KInstance *instance = KGlobal::_instance; + const KAboutData *about = instance ? instance->aboutData() : 0; + if (about) { + if (about->internalVersion()) { + argv[i++] = "--appversion"; + argv[i++] = about->internalVersion(); + } + + if (about->internalProgramName()) { + argv[i++] = "--programname"; + argv[i++] = about->internalProgramName(); + } + + if (about->internalBugAddress()) { + argv[i++] = "--bugaddress"; + argv[i++] = about->internalBugAddress(); + } + } + + if ( kapp && !kapp->startupId().isNull()) { + argv[i++] = "--startupid"; + argv[i++] = kapp->startupId().data(); + } + + if ( safer ) + argv[i++] = "--safer"; + + // NULL terminated list + argv[i] = NULL; + + startDrKonqi( argv, i ); + _exit(253); + + } + else { + fprintf(stderr, "Unknown appname\n"); + } + } + + if (crashRecursionCounter < 4) + { + fprintf(stderr, "Unable to start Dr. Konqi\n"); + } +#endif //Q_OS_UNIX + + _exit(255); +} + +#ifdef Q_OS_UNIX + +// Since we can't fork() in the crashhandler, we cannot execute any external code +// (there can be functions registered to be performed before fork(), for example +// handling of malloc locking, which doesn't work when malloc crashes because of heap corruption). + +static int write_socket(int sock, char *buffer, int len); +static int read_socket(int sock, char *buffer, int len); +static int openSocket(); + +void KCrash::startDrKonqi( const char* argv[], int argc ) +{ + int socket = openSocket(); + if( socket < -1 ) + { + startDirectly( argv, argc ); + return; + } + klauncher_header header; + header.cmd = LAUNCHER_EXEC_NEW; + const int BUFSIZE = 8192; // make sure this is big enough + char buffer[ BUFSIZE + 10 ]; + int pos = 0; + long argcl = argc; + memcpy( buffer + pos, &argcl, sizeof( argcl )); + pos += sizeof( argcl ); + for( int i = 0; + i < argc; + ++i ) + { + int len = strlen( argv[ i ] ) + 1; // include terminating \0 + if( pos + len > BUFSIZE ) + { + fprintf( stderr, "BUFSIZE in KCrash not big enough!\n" ); + startDirectly( argv, argc ); + return; + } + memcpy( buffer + pos, argv[ i ], len ); + pos += len; + } + long env = 0; + memcpy( buffer + pos, &env, sizeof( env )); + pos += sizeof( env ); + long avoid_loops = 0; + memcpy( buffer + pos, &avoid_loops, sizeof( avoid_loops )); + pos += sizeof( avoid_loops ); + header.arg_length = pos; + write_socket(socket, (char *) &header, sizeof(header)); + write_socket(socket, buffer, pos); + if( read_socket( socket, (char *) &header, sizeof(header)) < 0 + || header.cmd != LAUNCHER_OK ) + { + startDirectly( argv, argc ); + return; + } + long pid; + read_socket(socket, buffer, header.arg_length); + pid = *((long *) buffer); + + alarm(0); // Seems we made it.... + + for(;;) + { + if( kill( pid, 0 ) < 0 ) + _exit(253); + sleep(1); + // the debugger should stop this process anyway + } +} + +// If we can't reach tdeinit we can still at least try to fork() +void KCrash::startDirectly( const char* argv[], int ) +{ + fprintf( stderr, "KCrash cannot reach tdeinit, launching directly.\n" ); + pid_t pid = fork(); + if (pid <= 0) + { + if(!geteuid() && setgid(getgid()) < 0) + _exit(253); + if(!geteuid() && setuid(getuid()) < 0) + _exit(253); + execvp("drkonqi", const_cast< char** >( argv )); + _exit(errno); + } + else + { + alarm(0); // Seems we made it.... + // wait for child to exit + waitpid(pid, NULL, 0); + _exit(253); + } +} + +// From now on this code is copy&pasted from kinit/wrapper.c : + +extern char **environ; + +static char *getDisplay() +{ + const char *display; + char *result; + char *screen; + char *colon; + char *i; +/* + don't test for a value from tqglobal.h but instead distinguish + Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS + on the commandline (which in tqglobal.h however triggers Q_WS_QWS, + but we don't want to include that here) (Simon) +#ifdef Q_WS_X11 + */ +#if !defined(QWS) + display = getenv("DISPLAY"); +#else + display = getenv("QWS_DISPLAY"); +#endif + if (!display || !*display) + { + display = ":0"; + } + result = (char*)malloc(strlen(display)+1); + if (result == NULL) + return NULL; + + strcpy(result, display); + screen = strrchr(result, '.'); + colon = strrchr(result, ':'); + if (screen && (screen > colon)) + *screen = '\0'; + while((i = strchr(result, ':'))) + *i = '_'; + return result; +} + +/* + * Write 'len' bytes from 'buffer' into 'sock'. + * returns 0 on success, -1 on failure. + */ +static int write_socket(int sock, char *buffer, int len) +{ + ssize_t result; + int bytes_left = len; + while ( bytes_left > 0) + { + result = write(sock, buffer, bytes_left); + if (result > 0) + { + buffer += result; + bytes_left -= result; + } + else if (result == 0) + return -1; + else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) + return -1; + } + return 0; +} + +/* + * Read 'len' bytes from 'sock' into 'buffer'. + * returns 0 on success, -1 on failure. + */ +static int read_socket(int sock, char *buffer, int len) +{ + ssize_t result; + int bytes_left = len; + while ( bytes_left > 0) + { + result = read(sock, buffer, bytes_left); + if (result > 0) + { + buffer += result; + bytes_left -= result; + } + else if (result == 0) + return -1; + else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) + return -1; + } + return 0; +} + +static int openSocket() +{ + kde_socklen_t socklen; + int s; + struct sockaddr_un server; +#define MAX_SOCK_FILE 255 + char sock_file[MAX_SOCK_FILE + 1]; + const char *home_dir = getenv("HOME"); + const char *kde_home = getenv("KDEHOME"); + char *display; + + sock_file[0] = sock_file[MAX_SOCK_FILE] = 0; + + if (!kde_home || !kde_home[0]) + { + kde_home = "~/.trinity/"; + } + + if (kde_home[0] == '~') + { + if (!home_dir || !home_dir[0]) + { + fprintf(stderr, "Warning: $HOME not set!\n"); + return -1; + } + if (strlen(home_dir) > (MAX_SOCK_FILE-100)) + { + fprintf(stderr, "Warning: Home directory path too long!\n"); + return -1; + } + kde_home++; + strncpy(sock_file, home_dir, MAX_SOCK_FILE); + } + strncat(sock_file, kde_home, MAX_SOCK_FILE - strlen(sock_file)); + + /** Strip trailing '/' **/ + if ( sock_file[strlen(sock_file)-1] == '/') + sock_file[strlen(sock_file)-1] = 0; + + strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file)); + if( getenv("XAUTHLOCALHOSTNAME")) + strncat(sock_file, getenv("XAUTHLOCALHOSTNAME"), MAX_SOCK_FILE - strlen(sock_file) - 1); + else if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) + { + perror("Warning: Could not determine hostname: "); + return -1; + } + sock_file[sizeof(sock_file)-1] = '\0'; + + /* append $DISPLAY */ + display = getDisplay(); + if (display == NULL) + { + fprintf(stderr, "Error: Could not determine display.\n"); + return -1; + } + + if (strlen(sock_file)+strlen(display)+strlen("/tdeinit_")+2 > MAX_SOCK_FILE) + { + fprintf(stderr, "Warning: Socket name will be too long.\n"); + free(display); + return -1; + } + strcat(sock_file, "/tdeinit_"); + strcat(sock_file, display); + free(display); + + if (strlen(sock_file) >= sizeof(server.sun_path)) + { + fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n"); + return -1; + } + + /* + * create the socket stream + */ + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + perror("Warning: socket() failed: "); + return -1; + } + + server.sun_family = AF_UNIX; + strcpy(server.sun_path, sock_file); + socklen = sizeof(server); + if(connect(s, (struct sockaddr *)&server, socklen) == -1) + { + perror("Warning: connect() failed: "); + close(s); + return -1; + } + return s; +} + +#endif // Q_OS_UNIX |