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