• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kcrash.cpp

Go to the documentation of this file.
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 "kcrash.h"
00029 #include <kcmdlineargs.h>
00030 #include <kstandarddirs.h>
00031 #include <config-kstandarddirs.h>
00032 
00033 #include <config.h>
00034 
00035 #include <string.h>
00036 #include <signal.h>
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <unistd.h>
00040 
00041 #include <sys/types.h>
00042 #include <sys/time.h>
00043 #include <sys/resource.h>
00044 #include <sys/wait.h>
00045 #include <sys/un.h>
00046 #include <sys/socket.h>
00047 #include <errno.h>
00048 
00049 #include <qwindowdefs.h>
00050 #include <kglobal.h>
00051 #include <kcomponentdata.h>
00052 #include <kaboutdata.h>
00053 #include <kdebug.h>
00054 #include <kapplication.h>
00055 
00056 #include <../kinit/klauncher_cmds.h>
00057 
00058 #if defined Q_WS_X11
00059 #include <qx11info_x11.h>
00060 #include <X11/Xlib.h>
00061 #endif
00062 
00063 static KCrash::HandlerType s_emergencySaveFunction = 0;
00064 static KCrash::HandlerType s_crashHandler = 0;
00065 static char *s_appName = 0;
00066 static char *s_autoRestartCommand = 0;
00067 static char *s_appPath = 0;
00068 static char *s_drkonqiPath = 0;
00069 static KCrash::CrashFlags s_flags = 0;
00070 static bool s_launchDrKonqi = true;
00071 
00072 namespace KCrash
00073 {
00074   void startDrKonqi( const char* argv[], int argc );
00075   void startDirectly( const char* argv[], int argc );
00076 }
00077 
00078 
00079 // This function sets the function which should be called when the
00080 // application crashes and the
00081 // application is asked to try to save its data.
00082 void
00083 KCrash::setEmergencySaveFunction (HandlerType saveFunction)
00084 {
00085   s_emergencySaveFunction = saveFunction;
00086 
00087   /*
00088    * We need at least the default crash handler for
00089    * emergencySaveFunction to be called
00090    */
00091   if (s_emergencySaveFunction && !s_crashHandler) {
00092       s_launchDrKonqi = false; // --nocrashhandler means "no drkonqi please", let's still honor that
00093       setCrashHandler(defaultCrashHandler);
00094   }
00095 }
00096 
00097 KCrash::HandlerType
00098 KCrash::emergencySaveFunction()
00099 {
00100     return s_emergencySaveFunction;
00101 }
00102 
00103 // Set the default crash handler in 10 seconds
00104 // This is used after an autorestart, the second instance of the application
00105 // is started with --nocrashhandler (no drkonqi, more precisely), and we
00106 // set the defaultCrashHandler (to handle autorestart) after 10s.
00107 // The delay is to see if we stay up for more than 10s time, to avoid infinite
00108 // respawning if the app crashes on startup.
00109 class KCrashDelaySetHandler : public QObject
00110 {
00111 public:
00112     KCrashDelaySetHandler() {
00113         startTimer(10000); // 10 s
00114     }
00115 protected:
00116     void timerEvent(QTimerEvent *event) {
00117         if (!s_crashHandler) // not set meanwhile
00118             KCrash::setCrashHandler(KCrash::defaultCrashHandler);
00119         killTimer(event->timerId());
00120         this->deleteLater();
00121     }
00122 };
00123 
00124 
00125 
00126 void
00127 KCrash::setFlags(KCrash::CrashFlags flags)
00128 {
00129     s_flags = flags;
00130     if (s_flags & AutoRestart) {
00131         // We need at least the default crash handler for autorestart to work.
00132         if (!s_crashHandler) {
00133             s_launchDrKonqi = false; // KDE_DEBUG=1 or --nocrashhandler means "no drkonqi please", let's still honor that
00134             KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
00135             if (!args->isSet("crashhandler")) // --nocrashhandler was passed, probably due to a crash, delay restart handler
00136                 new KCrashDelaySetHandler;
00137             else // probably because KDE_DEBUG=1. set restart handler immediately.
00138                 setCrashHandler(defaultCrashHandler);
00139         }
00140     }
00141 }
00142 
00143 void
00144 KCrash::setApplicationPath(const QString& path)
00145 {
00146     s_appPath = qstrdup(path.toLatin1().constData());
00147 }
00148 
00149 void
00150 KCrash::setApplicationName(const QString& name)
00151 {
00152     s_appName = qstrdup(name.toLatin1().constData());
00153         s_autoRestartCommand = qstrdup(QString(name + " --nocrashhandler &").toLatin1().constData());
00154 }
00155 
00156 // This function sets the function which should be responsible for
00157 // the application crash handling.
00158 void
00159 KCrash::setCrashHandler (HandlerType handler)
00160 {
00161 #ifdef Q_OS_UNIX
00162   if (!handler)
00163     handler = SIG_DFL;
00164 
00165   sigset_t mask;
00166   sigemptyset(&mask);
00167 
00168 #ifdef SIGSEGV
00169   signal (SIGSEGV, handler);
00170   sigaddset(&mask, SIGSEGV);
00171 #endif
00172 #ifdef SIGFPE
00173   signal (SIGFPE, handler);
00174   sigaddset(&mask, SIGFPE);
00175 #endif
00176 #ifdef SIGILL
00177   signal (SIGILL, handler);
00178   sigaddset(&mask, SIGILL);
00179 #endif
00180 #ifdef SIGABRT
00181   signal (SIGABRT, handler);
00182   sigaddset(&mask, SIGABRT);
00183 #endif
00184 
00185   sigprocmask(SIG_UNBLOCK, &mask, 0);
00186 #endif //Q_OS_UNIX
00187 
00188   s_crashHandler = handler;
00189 
00190   if (!s_drkonqiPath && handler == defaultCrashHandler)
00191     s_drkonqiPath = qstrdup(KStandardDirs::findExe("drkonqi").toLatin1().constData());
00192 }
00193 
00194 KCrash::HandlerType
00195 KCrash::crashHandler()
00196 {
00197     return s_crashHandler;
00198 }
00199 
00200 static void
00201 closeAllFDs()
00202 {
00203   // Close all remaining file descriptors except for stdin/stdout/stderr
00204   struct rlimit rlp;
00205   getrlimit(RLIMIT_NOFILE, &rlp);
00206   for (int i = 3; i < (int)rlp.rlim_cur; i++)
00207     close(i);
00208 }
00209 
00210 void
00211 KCrash::defaultCrashHandler (int sig)
00212 {
00213 #ifdef Q_OS_UNIX
00214   // WABA: Do NOT use kDebug() in this function because it is much too risky!
00215   // Handle possible recursions
00216   static int crashRecursionCounter = 0;
00217   crashRecursionCounter++; // Nothing before this, please !
00218 
00219   signal(SIGALRM, SIG_DFL);
00220   alarm(3); // Kill me... (in case we deadlock in malloc)
00221 
00222   if (crashRecursionCounter < 2) {
00223     if (s_emergencySaveFunction) {
00224       s_emergencySaveFunction (sig);
00225     }
00226     if ((s_flags & AutoRestart) && s_autoRestartCommand) {
00227         sleep(1);
00228         setCrashHandler(0); // make sure the new process doesn't inherit SIG_IGN from us
00229         system(s_autoRestartCommand);
00230     }
00231     crashRecursionCounter++; //
00232   }
00233 
00234   if (!(s_flags & KeepFDs))
00235     closeAllFDs();
00236 #if defined(Q_WS_X11)
00237   else if (QX11Info::display())
00238     close(ConnectionNumber(QX11Info::display()));
00239 #endif
00240 
00241   // this code is leaking, but this should not hurt cause we will do a
00242   // exec() afterwards. exec() is supposed to clean up.
00243     if (crashRecursionCounter < 3)
00244     {
00245 #ifndef NDEBUG
00246         fprintf(stderr, "KCrash: crashing... crashRecursionCounter = %d\n",
00247         crashRecursionCounter);
00248         fprintf(stderr, "KCrash: Application Name = %s path = %s pid = %d\n",
00249         s_appName ? s_appName : "<unknown>" ,
00250         s_appPath ? s_appPath : "<unknown>", getpid());
00251 #else
00252         fprintf(stderr, "KCrash: Application '%s' crashing...\n",
00253         s_appName ? s_appName : "<unknown>");
00254 #endif
00255 
00256         if (!s_launchDrKonqi) {
00257             setCrashHandler(0);
00258             raise(sig); // dump core, or whatever is the default action for this signal.
00259             return;
00260         }
00261 
00262           const char * argv[24]; // don't forget to update this
00263           int i = 0;
00264 
00265           // argument 0 has to be drkonqi
00266           argv[i++] = s_drkonqiPath;
00267 
00268 #if defined Q_WS_X11
00269           // start up on the correct display
00270           argv[i++] = "-display";
00271           if ( QX11Info::display() )
00272             argv[i++] = XDisplayString(QX11Info::display());
00273           else
00274             argv[i++] = getenv("DISPLAY");
00275 #elif defined(Q_WS_QWS)
00276           // start up on the correct display
00277           argv[i++] = "-display";
00278           argv[i++] = getenv("QWS_DISPLAY");
00279 #endif
00280 
00281           argv[i++] = "--appname";
00282           argv[i++] = s_appName ? s_appName : "<unknown>";
00283 
00284           if (KApplication::loadedByKdeinit)
00285             argv[i++] = "--kdeinit";
00286 
00287           // only add apppath if it's not NULL
00288           if (s_appPath && *s_appPath) {
00289             argv[i++] = "--apppath";
00290             argv[i++] = s_appPath;
00291           }
00292 
00293           // signal number -- will never be NULL
00294           char sigtxt[ 10 ];
00295           sprintf( sigtxt, "%d", sig );
00296           argv[i++] = "--signal";
00297           argv[i++] = sigtxt;
00298 
00299           char pidtxt[ 10 ];
00300           sprintf( pidtxt, "%d", getpid());
00301           argv[i++] = "--pid";
00302           argv[i++] = pidtxt;
00303 
00304           const KComponentData componentData = KGlobal::mainComponent();
00305           const KAboutData *about = componentData.isValid() ? componentData.aboutData() : 0;
00306           if (about) {
00307             if (about->internalVersion()) {
00308               argv[i++] = "--appversion";
00309               argv[i++] = about->internalVersion();
00310             }
00311 
00312             if (about->internalProgramName()) {
00313               argv[i++] = "--programname";
00314               argv[i++] = about->internalProgramName();
00315             }
00316 
00317             if (about->internalBugAddress()) {
00318               argv[i++] = "--bugaddress";
00319               argv[i++] = about->internalBugAddress();
00320             }
00321           }
00322 
00323           char sidtxt[256];
00324           if ( kapp && !kapp->startupId().isNull()) {
00325             argv[i++] = "--startupid";
00326             strlcpy(sidtxt, kapp->startupId().constData(), sizeof(sidtxt));
00327             argv[i++] = sidtxt;
00328           }
00329 
00330           if ( s_flags & SaferDialog )
00331             argv[i++] = "--safer";
00332 
00333           // NULL terminated list
00334           argv[i] = NULL;
00335 
00336           if (!(s_flags & AlwaysDirectly)) {
00337             startDrKonqi( argv, i );
00338             fprintf( stderr, "KCrash cannot reach kdeinit, launching directly.\n" );
00339           }
00340           startDirectly( argv, i );
00341     }
00342 
00343     if (crashRecursionCounter < 4)
00344     {
00345       fprintf(stderr, "Unable to start Dr. Konqi\n");
00346     }
00347 #endif //Q_OS_UNIX
00348 
00349   _exit(255);
00350 }
00351 
00352 #ifdef Q_OS_UNIX
00353 
00354 // Since we can't fork() in the crashhandler, we cannot execute any external code
00355 // (there can be functions registered to be performed before fork(), for example
00356 // handling of malloc locking, which doesn't work when malloc crashes because of heap corruption).
00357 
00358 static int write_socket(int sock, char *buffer, int len);
00359 static int read_socket(int sock, char *buffer, int len);
00360 static int openSocket();
00361 
00362 void KCrash::startDrKonqi( const char* argv[], int argc )
00363 {
00364   int socket = openSocket();
00365   if( socket < -1 )
00366     return;
00367   klauncher_header header;
00368   header.cmd = LAUNCHER_EXEC_NEW;
00369   const int BUFSIZE = 8192; // make sure this is big enough
00370   char buffer[ BUFSIZE + 10 ];
00371   int pos = 0;
00372   long argcl = argc;
00373   memcpy( buffer + pos, &argcl, sizeof( argcl ));
00374   pos += sizeof( argcl );
00375   for( int i = 0;
00376        i < argc;
00377        ++i )
00378   {
00379     int len = strlen( argv[ i ] ) + 1; // include terminating \0
00380     if( pos + len >= BUFSIZE )
00381     {
00382       fprintf( stderr, "BUFSIZE in KCrash not big enough!\n" );
00383       return;
00384     }
00385     memcpy( buffer + pos, argv[ i ], len );
00386     pos += len;
00387   }
00388   long env = 0;
00389   memcpy( buffer + pos, &env, sizeof( env ));
00390   pos += sizeof( env );
00391   long avoid_loops = 0;
00392   memcpy( buffer + pos, &avoid_loops, sizeof( avoid_loops ));
00393   pos += sizeof( avoid_loops );
00394   header.arg_length = pos;
00395   write_socket(socket, (char *) &header, sizeof(header));
00396   write_socket(socket, buffer, pos);
00397   if( read_socket( socket, (char *) &header, sizeof(header)) < 0
00398       || header.cmd != LAUNCHER_OK )
00399   {
00400     return;
00401   }
00402   long pid;
00403   read_socket(socket, buffer, header.arg_length);
00404   pid = *((long *) buffer);
00405 
00406   alarm(0); // Seems we made it....
00407 
00408   for(;;)
00409   {
00410     if( kill( pid, 0 ) < 0 )
00411       _exit(253);
00412     sleep( 1 ); // the debugger should stop this process anyway
00413   }
00414 }
00415 
00416 // If we can't reach kdeinit we can still at least try to fork()
00417 void KCrash::startDirectly( const char* argv[], int )
00418 {
00419   pid_t pid = fork();
00420   switch (pid)
00421   {
00422   case -1:
00423     fprintf( stderr, "KCrash failed to fork(), errno = %d\n", errno );
00424     _exit(253);
00425   case 0:
00426     if (setgid(getgid()) < 0 || setuid(getuid()) < 0)
00427       _exit(253); // This cannot happen. Theoretically.
00428     if (s_flags & KeepFDs)
00429       closeAllFDs();
00430     execvp(s_drkonqiPath, const_cast< char** >( argv ));
00431     fprintf( stderr, "KCrash failed to exec(), errno = %d\n", errno );
00432     _exit(253);
00433   default:
00434     alarm(0); // Seems we made it....
00435     // wait for child to exit
00436     waitpid(pid, NULL, 0);
00437     _exit(253);
00438   }
00439 }
00440 
00441 // From now on this code is copy&pasted from kinit/wrapper.c :
00442 
00443 extern char **environ;
00444 
00445 static char *getDisplay()
00446 {
00447    const char *display;
00448    char *result;
00449    char *screen;
00450    char *colon;
00451    char *i;
00452 /*
00453  don't test for a value from qglobal.h but instead distinguish
00454  Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS
00455  on the commandline (which in qglobal.h however triggers Q_WS_QWS,
00456  but we don't want to include that here) (Simon)
00457 #ifdef Q_WS_X11
00458  */
00459 #ifdef NO_DISPLAY
00460    display = "NODISPLAY";
00461 #elif !defined(QWS)
00462    display = getenv("DISPLAY");
00463 #else
00464    display = getenv("QWS_DISPLAY");
00465 #endif
00466    if (!display || !*display)
00467    {
00468       display = ":0";
00469    }
00470    result = (char*)malloc(strlen(display)+1);
00471    if (result == NULL)
00472       return NULL;
00473 
00474    strcpy(result, display);
00475    screen = strrchr(result, '.');
00476    colon = strrchr(result, ':');
00477    if (screen && (screen > colon))
00478       *screen = '\0';
00479    while((i = strchr(result, ':')))
00480      *i = '_';
00481    return result;
00482 }
00483 
00484 /*
00485  * Write 'len' bytes from 'buffer' into 'sock'.
00486  * returns 0 on success, -1 on failure.
00487  */
00488 static int write_socket(int sock, char *buffer, int len)
00489 {
00490   ssize_t result;
00491   int bytes_left = len;
00492   while ( bytes_left > 0)
00493   {
00494      result = write(sock, buffer, bytes_left);
00495      if (result > 0)
00496      {
00497         buffer += result;
00498         bytes_left -= result;
00499      }
00500      else if (result == 0)
00501         return -1;
00502      else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00503         return -1;
00504   }
00505   return 0;
00506 }
00507 
00508 /*
00509  * Read 'len' bytes from 'sock' into 'buffer'.
00510  * returns 0 on success, -1 on failure.
00511  */
00512 static int read_socket(int sock, char *buffer, int len)
00513 {
00514   ssize_t result;
00515   int bytes_left = len;
00516   while ( bytes_left > 0)
00517   {
00518      result = read(sock, buffer, bytes_left);
00519      if (result > 0)
00520      {
00521         buffer += result;
00522         bytes_left -= result;
00523      }
00524      else if (result == 0)
00525         return -1;
00526      else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00527         return -1;
00528   }
00529   return 0;
00530 }
00531 
00532 static int openSocket()
00533 {
00534   kde_socklen_t socklen;
00535   int s;
00536   struct sockaddr_un server;
00537 #define MAX_SOCK_FILE 255
00538   char sock_file[MAX_SOCK_FILE + 1];
00539   const char *home_dir = getenv("HOME");
00540   const char *kde_home = getenv("KDEHOME");
00541   char *display;
00542 
00543   sock_file[0] = sock_file[MAX_SOCK_FILE] = 0;
00544 
00545   if (!kde_home || !kde_home[0])
00546   {
00547      kde_home = "~/" KDE_DEFAULT_HOME "/";
00548   }
00549 
00550   if (kde_home[0] == '~')
00551   {
00552      if (!home_dir || !home_dir[0])
00553      {
00554         fprintf(stderr, "Warning: $HOME not set!\n");
00555         return -1;
00556      }
00557      if (strlen(home_dir) > (MAX_SOCK_FILE-100))
00558      {
00559         fprintf(stderr, "Warning: Home directory path too long!\n");
00560         return -1;
00561      }
00562      kde_home++;
00563      strlcpy(sock_file, home_dir, MAX_SOCK_FILE);
00564   }
00565   strlcat(sock_file, kde_home, MAX_SOCK_FILE);
00566 
00568   if ( sock_file[strlen(sock_file)-1] == '/')
00569      sock_file[strlen(sock_file)-1] = 0;
00570 
00571   strlcat(sock_file, "/socket-", MAX_SOCK_FILE);
00572   if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0)
00573   {
00574      perror("Warning: Could not determine hostname: ");
00575      return -1;
00576   }
00577   sock_file[sizeof(sock_file)-1] = '\0';
00578 
00579   /* append $DISPLAY */
00580   display = getDisplay();
00581   if (display == NULL)
00582   {
00583      fprintf(stderr, "Error: Could not determine display.\n");
00584      return -1;
00585   }
00586 
00587   if (strlen(sock_file)+strlen(display)+strlen("/kdeinit4_")+2 > MAX_SOCK_FILE)
00588   {
00589      fprintf(stderr, "Warning: Socket name will be too long.\n");
00590      free(display);
00591      return -1;
00592   }
00593   strcat(sock_file, "/kdeinit4_");
00594   strcat(sock_file, display);
00595   free(display);
00596 
00597   if (strlen(sock_file) >= sizeof(server.sun_path))
00598   {
00599      fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
00600      return -1;
00601   }
00602 
00603   /*
00604    * create the socket stream
00605    */
00606   s = socket(PF_UNIX, SOCK_STREAM, 0);
00607   if (s < 0)
00608   {
00609      perror("Warning: socket() failed: ");
00610      return -1;
00611   }
00612 
00613   server.sun_family = AF_UNIX;
00614   strcpy(server.sun_path, sock_file);
00615   printf("sock_file=%s\n", sock_file);
00616   socklen = sizeof(server);
00617   if(connect(s, (struct sockaddr *)&server, socklen) == -1)
00618   {
00619      perror("Warning: connect() failed: ");
00620      close(s);
00621      return -1;
00622   }
00623   return s;
00624 }
00625 
00626 #endif // Q_OS_UNIX

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal