kcrash.cpp

00001 /*
00002  * This file is part of the KDE Libraries
00003  * Copyright (C) 2000 Timo Hummel <timo.hummel@sap.com>
00004  *                    Tom Braun <braunt@fh-konstanz.de>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Library General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Library General Public License
00017  * along with this library; see the file COPYING.LIB.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  */
00021 
00022 /*
00023  * This file is used to catch signals which would normally
00024  * crash the application (like segmentation fault, floating
00025  * point exception and such).
00026  */
00027 
00028 #include "config.h"
00029 
00030 #include <string.h>
00031 #include <signal.h>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <unistd.h>
00035 #include "kcrash.h"
00036 
00037 #include <sys/types.h>
00038 #include <sys/time.h>
00039 #include <sys/resource.h>
00040 #include <sys/wait.h>
00041 #include <sys/un.h>
00042 #include <sys/socket.h>
00043 #include <errno.h>
00044 
00045 #include <qwindowdefs.h>
00046 #include <kglobal.h>
00047 #include <kinstance.h>
00048 #include <kaboutdata.h>
00049 #include <kdebug.h>
00050 #include <kapplication.h>
00051 #include <dcopclient.h>
00052 
00053 #include <../kinit/klauncher_cmds.h>
00054 
00055 #if defined Q_WS_X11
00056 #include <X11/Xlib.h>
00057 #endif
00058 
00059 KCrash::HandlerType KCrash::_emergencySaveFunction = 0;
00060 KCrash::HandlerType KCrash::_crashHandler = 0;
00061 const char *KCrash::appName = 0;
00062 const char *KCrash::appPath = 0;
00063 bool KCrash::safer = false;
00064 
00065 // This function sets the function which should be called when the
00066 // application crashes and the
00067 // application is asked to try to save its data.
00068 void
00069 KCrash::setEmergencySaveFunction (HandlerType saveFunction)
00070 {
00071   _emergencySaveFunction = saveFunction;
00072 
00073   /*
00074    * We need at least the default crash handler for
00075    * emergencySaveFunction to be called
00076    */
00077   if (_emergencySaveFunction && !_crashHandler)
00078     _crashHandler = defaultCrashHandler;
00079 }
00080 
00081 
00082 // This function sets the function which should be responsible for
00083 // the application crash handling.
00084 void
00085 KCrash::setCrashHandler (HandlerType handler)
00086 {
00087 #ifdef Q_OS_UNIX
00088   if (!handler)
00089     handler = SIG_DFL;
00090 
00091   sigset_t mask;
00092   sigemptyset(&mask);
00093 
00094 #ifdef SIGSEGV
00095   signal (SIGSEGV, handler);
00096   sigaddset(&mask, SIGSEGV);
00097 #endif
00098 #ifdef SIGFPE
00099   signal (SIGFPE, handler);
00100   sigaddset(&mask, SIGFPE);
00101 #endif
00102 #ifdef SIGILL
00103   signal (SIGILL, handler);
00104   sigaddset(&mask, SIGILL);
00105 #endif
00106 #ifdef SIGABRT
00107   signal (SIGABRT, handler);
00108   sigaddset(&mask, SIGABRT);
00109 #endif
00110 
00111   sigprocmask(SIG_UNBLOCK, &mask, 0);
00112 #endif //Q_OS_UNIX
00113 
00114   _crashHandler = handler;
00115 }
00116 
00117 void
00118 KCrash::defaultCrashHandler (int sig)
00119 {
00120 #ifdef Q_OS_UNIX
00121   // WABA: Do NOT use kdDebug() in this function because it is much too risky!
00122   // Handle possible recursions
00123   static int crashRecursionCounter = 0;
00124   crashRecursionCounter++; // Nothing before this, please !
00125 
00126   signal(SIGALRM, SIG_DFL);
00127   alarm(3); // Kill me... (in case we deadlock in malloc)
00128 
00129   if (crashRecursionCounter < 2) {
00130     if (_emergencySaveFunction) {
00131       _emergencySaveFunction (sig);
00132     }
00133     crashRecursionCounter++; //
00134   }
00135 
00136   // Close all remaining file descriptors except for stdin/stdout/stderr
00137   struct rlimit rlp;
00138   getrlimit(RLIMIT_NOFILE, &rlp);
00139   for (int i = 3; i < (int)rlp.rlim_cur; i++)
00140     close(i);
00141 
00142 
00143   // this code is leaking, but this should not hurt cause we will do a
00144   // exec() afterwards. exec() is supposed to clean up.
00145     if (crashRecursionCounter < 3)
00146     {
00147       if (appName)
00148       {
00149 #ifndef NDEBUG
00150         fprintf(stderr, "KCrash: crashing... crashRecursionCounter = %d\n", crashRecursionCounter);
00151         fprintf(stderr, "KCrash: Application Name = %s path = %s pid = %d\n", appName ? appName : "<unknown>" , appPath ? appPath : "<unknown>", getpid());
00152 #else
00153         fprintf(stderr, "KCrash: Application '%s' crashing...\n", appName ? appName : "<unknown>");
00154 #endif
00155 
00156           const char * argv[24]; // don't forget to update this
00157           int i = 0;
00158 
00159           // argument 0 has to be drkonqi
00160           argv[i++] = "drkonqi";
00161 
00162 #if defined Q_WS_X11
00163           // start up on the correct display
00164           argv[i++] = "-display";
00165           if ( qt_xdisplay() )
00166             argv[i++] = XDisplayString(qt_xdisplay());
00167           else
00168             argv[i++] = getenv("DISPLAY");
00169 #elif defined(Q_WS_QWS)
00170           // start up on the correct display
00171           argv[i++] = "-display";
00172           argv[i++] = getenv("QWS_DISPLAY");
00173 #endif
00174 
00175           // we have already tested this
00176           argv[i++] = "--appname";
00177           argv[i++] = appName;
00178           if (KApplication::loadedByKdeinit)
00179             argv[i++] = "--kdeinit";
00180 
00181           // only add apppath if it's not NULL
00182           if (appPath) {
00183             argv[i++] = "--apppath";
00184             argv[i++] = appPath;
00185           }
00186 
00187           // signal number -- will never be NULL
00188           char sigtxt[ 10 ];
00189           sprintf( sigtxt, "%d", sig );
00190           argv[i++] = "--signal";
00191           argv[i++] = sigtxt;
00192 
00193           char pidtxt[ 10 ];
00194           sprintf( pidtxt, "%d", getpid());
00195           argv[i++] = "--pid";
00196           argv[i++] = pidtxt;
00197 
00198           const KInstance *instance = KGlobal::_instance;
00199           const KAboutData *about = instance ? instance->aboutData() : 0;
00200           if (about) {
00201             if (about->internalVersion()) {
00202               argv[i++] = "--appversion";
00203               argv[i++] = about->internalVersion();
00204             }
00205 
00206             if (about->internalProgramName()) {
00207               argv[i++] = "--programname";
00208               argv[i++] = about->internalProgramName();
00209             }
00210 
00211             if (about->internalBugAddress()) {
00212               argv[i++] = "--bugaddress";
00213               argv[i++] = about->internalBugAddress();
00214             }
00215           }
00216 
00217           if ( kapp && !kapp->startupId().isNull()) {
00218             argv[i++] = "--startupid";
00219             argv[i++] = kapp->startupId().data();
00220           }
00221 
00222           if ( safer )
00223             argv[i++] = "--safer";
00224 
00225           // NULL terminated list
00226           argv[i] = NULL;
00227 
00228           startDrKonqi( argv, i );
00229           _exit(253);
00230 
00231       }
00232       else {
00233         fprintf(stderr, "Unknown appname\n");
00234       }
00235     }
00236 
00237     if (crashRecursionCounter < 4)
00238     {
00239       fprintf(stderr, "Unable to start Dr. Konqi\n");
00240     }
00241 #endif //Q_OS_UNIX
00242 
00243   _exit(255);
00244 }
00245 
00246 #ifdef Q_OS_UNIX
00247 
00248 // Since we can't fork() in the crashhandler, we cannot execute any external code
00249 // (there can be functions registered to be performed before fork(), for example
00250 // handling of malloc locking, which doesn't work when malloc crashes because of heap corruption).
00251 
00252 static int write_socket(int sock, char *buffer, int len);
00253 static int read_socket(int sock, char *buffer, int len);
00254 static int openSocket();
00255 
00256 void KCrash::startDrKonqi( const char* argv[], int argc )
00257 {
00258   int socket = openSocket();
00259   if( socket < -1 )
00260   {
00261     startDirectly( argv, argc );
00262     return;
00263   }
00264   klauncher_header header;
00265   header.cmd = LAUNCHER_EXEC_NEW;
00266   const int BUFSIZE = 8192; // make sure this is big enough
00267   char buffer[ BUFSIZE + 10 ];
00268   int pos = 0;
00269   long argcl = argc;
00270   memcpy( buffer + pos, &argcl, sizeof( argcl ));
00271   pos += sizeof( argcl );
00272   for( int i = 0;
00273        i < argc;
00274        ++i )
00275   {
00276     int len = strlen( argv[ i ] ) + 1; // include terminating \0
00277     if( pos + len > BUFSIZE )
00278     {
00279       fprintf( stderr, "BUFSIZE in KCrash not big enough!\n" );
00280       startDirectly( argv, argc );
00281       return;
00282     }
00283     memcpy( buffer + pos, argv[ i ], len );
00284     pos += len;
00285   }
00286   long env = 0;
00287   memcpy( buffer + pos, &env, sizeof( env ));
00288   pos += sizeof( env );
00289   long avoid_loops = 0;
00290   memcpy( buffer + pos, &avoid_loops, sizeof( avoid_loops ));
00291   pos += sizeof( avoid_loops );
00292   header.arg_length = pos;
00293   write_socket(socket, (char *) &header, sizeof(header));
00294   write_socket(socket, buffer, pos);
00295   if( read_socket( socket, (char *) &header, sizeof(header)) < 0
00296       || header.cmd != LAUNCHER_OK )
00297   {
00298     startDirectly( argv, argc );
00299     return;
00300   }
00301   long pid;
00302   read_socket(socket, buffer, header.arg_length);
00303   pid = *((long *) buffer);
00304 
00305   alarm(0); // Seems we made it....
00306 
00307   for(;;)
00308   {
00309     if( kill( pid, 0 ) < 0 )
00310       _exit(253);
00311     sleep( 1 ); // the debugger should stop this process anyway
00312   }
00313 }
00314 
00315 // If we can't reach kdeinit we can still at least try to fork()
00316 void KCrash::startDirectly( const char* argv[], int )
00317 {
00318   fprintf( stderr, "KCrash cannot reach kdeinit, launching directly.\n" );
00319   pid_t pid = fork();
00320   if (pid <= 0)
00321   {
00322     if(!geteuid() && setgid(getgid()) < 0)
00323       _exit(253);
00324     if(!geteuid() && setuid(getuid()) < 0)
00325       _exit(253);
00326     execvp("drkonqi", const_cast< char** >( argv ));
00327     _exit(errno);
00328   }
00329   else
00330   {
00331     alarm(0); // Seems we made it....
00332     // wait for child to exit
00333     waitpid(pid, NULL, 0);
00334     _exit(253);
00335   }
00336 }
00337 
00338 // From now on this code is copy&pasted from kinit/wrapper.c :
00339 
00340 extern char **environ;
00341 
00342 static char *getDisplay()
00343 {
00344    const char *display;
00345    char *result;
00346    char *screen;
00347    char *colon;
00348    char *i;
00349 /*
00350  don't test for a value from qglobal.h but instead distinguish
00351  Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS
00352  on the commandline (which in qglobal.h however triggers Q_WS_QWS,
00353  but we don't want to include that here) (Simon)
00354 #ifdef Q_WS_X11
00355  */
00356 #if !defined(QWS)
00357    display = getenv("DISPLAY");
00358 #else
00359    display = getenv("QWS_DISPLAY");
00360 #endif
00361    if (!display || !*display)
00362    {
00363       display = ":0";
00364    }
00365    result = (char*)malloc(strlen(display)+1);
00366    if (result == NULL)
00367       return NULL;
00368 
00369    strcpy(result, display);
00370    screen = strrchr(result, '.');
00371    colon = strrchr(result, ':');
00372    if (screen && (screen > colon))
00373       *screen = '\0';
00374    while((i = strchr(result, ':')))
00375      *i = '_';
00376    return result;
00377 }
00378 
00379 /*
00380  * Write 'len' bytes from 'buffer' into 'sock'.
00381  * returns 0 on success, -1 on failure.
00382  */
00383 static int write_socket(int sock, char *buffer, int len)
00384 {
00385   ssize_t result;
00386   int bytes_left = len;
00387   while ( bytes_left > 0)
00388   {
00389      result = write(sock, buffer, bytes_left);
00390      if (result > 0)
00391      {
00392         buffer += result;
00393         bytes_left -= result;
00394      }
00395      else if (result == 0)
00396         return -1;
00397      else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00398         return -1;
00399   }
00400   return 0;
00401 }
00402 
00403 /*
00404  * Read 'len' bytes from 'sock' into 'buffer'.
00405  * returns 0 on success, -1 on failure.
00406  */
00407 static int read_socket(int sock, char *buffer, int len)
00408 {
00409   ssize_t result;
00410   int bytes_left = len;
00411   while ( bytes_left > 0)
00412   {
00413      result = read(sock, buffer, bytes_left);
00414      if (result > 0)
00415      {
00416         buffer += result;
00417         bytes_left -= result;
00418      }
00419      else if (result == 0)
00420         return -1;
00421      else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00422         return -1;
00423   }
00424   return 0;
00425 }
00426 
00427 static int openSocket()
00428 {
00429   kde_socklen_t socklen;
00430   int s;
00431   struct sockaddr_un server;
00432 #define MAX_SOCK_FILE 255
00433   char sock_file[MAX_SOCK_FILE + 1];
00434   const char *home_dir = getenv("HOME");
00435   const char *kde_home = getenv("KDEHOME");
00436   char *display;
00437 
00438   sock_file[0] = sock_file[MAX_SOCK_FILE] = 0;
00439 
00440   if (!kde_home || !kde_home[0])
00441   {
00442      kde_home = "~/.kde/";
00443   }
00444 
00445   if (kde_home[0] == '~')
00446   {
00447      if (!home_dir || !home_dir[0])
00448      {
00449         fprintf(stderr, "Warning: $HOME not set!\n");
00450         return -1;
00451      }
00452      if (strlen(home_dir) > (MAX_SOCK_FILE-100))
00453      {
00454         fprintf(stderr, "Warning: Home directory path too long!\n");
00455         return -1;
00456      }
00457      kde_home++;
00458      strncpy(sock_file, home_dir, MAX_SOCK_FILE);
00459   }
00460   strncat(sock_file, kde_home, MAX_SOCK_FILE - strlen(sock_file));
00461 
00463   if ( sock_file[strlen(sock_file)-1] == '/')
00464      sock_file[strlen(sock_file)-1] = 0;
00465   
00466   strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file));
00467   if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0)
00468   {
00469      perror("Warning: Could not determine hostname: ");
00470      return -1;
00471   }
00472   sock_file[sizeof(sock_file)-1] = '\0';
00473 
00474   /* append $DISPLAY */
00475   display = getDisplay();
00476   if (display == NULL)
00477   {
00478      fprintf(stderr, "Error: Could not determine display.\n");
00479      return -1;
00480   }
00481 
00482   if (strlen(sock_file)+strlen(display)+strlen("/kdeinit_")+2 > MAX_SOCK_FILE)
00483   {
00484      fprintf(stderr, "Warning: Socket name will be too long.\n");
00485      free(display);
00486      return -1;
00487   }
00488   strcat(sock_file, "/kdeinit_");
00489   strcat(sock_file, display);
00490   free(display);
00491 
00492   if (strlen(sock_file) >= sizeof(server.sun_path))
00493   {
00494      fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
00495      return -1;
00496   }
00497 
00498   /*
00499    * create the socket stream
00500    */
00501   s = socket(PF_UNIX, SOCK_STREAM, 0);
00502   if (s < 0) 
00503   {
00504      perror("Warning: socket() failed: ");
00505      return -1;
00506   }
00507 
00508   server.sun_family = AF_UNIX;
00509   strcpy(server.sun_path, sock_file);
00510   socklen = sizeof(server);
00511   if(connect(s, (struct sockaddr *)&server, socklen) == -1) 
00512   {
00513      perror("Warning: connect() failed: ");
00514      close(s);
00515      return -1;
00516   }
00517   return s;
00518 }
00519 
00520 #endif // Q_OS_UNIX
KDE Home | KDE Accessibility Home | Description of Access Keys