00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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 = false;
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
00080
00081
00082 void
00083 KCrash::setEmergencySaveFunction (HandlerType saveFunction)
00084 {
00085 s_emergencySaveFunction = saveFunction;
00086
00087
00088
00089
00090
00091 if (s_emergencySaveFunction && !s_crashHandler) {
00092 s_launchDrKonqi = false;
00093 setCrashHandler(defaultCrashHandler);
00094 }
00095 }
00096
00097 KCrash::HandlerType
00098 KCrash::emergencySaveFunction()
00099 {
00100 return s_emergencySaveFunction;
00101 }
00102
00103
00104
00105
00106
00107
00108
00109 class KCrashDelaySetHandler : public QObject
00110 {
00111 public:
00112 KCrashDelaySetHandler() {
00113 startTimer(10000);
00114 }
00115 protected:
00116 void timerEvent(QTimerEvent *event) {
00117 if (!s_crashHandler)
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
00132 if (!s_crashHandler) {
00133 s_launchDrKonqi = false;
00134 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
00135 if (!args->isSet("crashhandler"))
00136 new KCrashDelaySetHandler;
00137 else
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
00157
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
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
00215
00216 static int crashRecursionCounter = 0;
00217 crashRecursionCounter++;
00218
00219 signal(SIGALRM, SIG_DFL);
00220 alarm(3);
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);
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
00242
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);
00259 return;
00260 }
00261
00262 const char * argv[24];
00263 int i = 0;
00264
00265
00266 argv[i++] = s_drkonqiPath;
00267
00268 #if defined Q_WS_X11
00269
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
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
00288 if (s_appPath && *s_appPath) {
00289 argv[i++] = "--apppath";
00290 argv[i++] = s_appPath;
00291 }
00292
00293
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
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
00355
00356
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;
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;
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);
00407
00408 for(;;)
00409 {
00410 if( kill( pid, 0 ) < 0 )
00411 _exit(253);
00412 sleep( 1 );
00413 }
00414 }
00415
00416
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);
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);
00435
00436 waitpid(pid, NULL, 0);
00437 _exit(253);
00438 }
00439 }
00440
00441
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
00454
00455
00456
00457
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 #ifdef __APPLE__
00482 while((i = strchr(result, '/')))
00483 *i = '_';
00484 #endif
00485 return result;
00486 }
00487
00488
00489
00490
00491
00492 static int write_socket(int sock, char *buffer, int len)
00493 {
00494 ssize_t result;
00495 int bytes_left = len;
00496 while ( bytes_left > 0)
00497 {
00498 result = write(sock, buffer, bytes_left);
00499 if (result > 0)
00500 {
00501 buffer += result;
00502 bytes_left -= result;
00503 }
00504 else if (result == 0)
00505 return -1;
00506 else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00507 return -1;
00508 }
00509 return 0;
00510 }
00511
00512
00513
00514
00515
00516 static int read_socket(int sock, char *buffer, int len)
00517 {
00518 ssize_t result;
00519 int bytes_left = len;
00520 while ( bytes_left > 0)
00521 {
00522 result = read(sock, buffer, bytes_left);
00523 if (result > 0)
00524 {
00525 buffer += result;
00526 bytes_left -= result;
00527 }
00528 else if (result == 0)
00529 return -1;
00530 else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
00531 return -1;
00532 }
00533 return 0;
00534 }
00535
00536 static int openSocket()
00537 {
00538 kde_socklen_t socklen;
00539 int s;
00540 struct sockaddr_un server;
00541 #define MAX_SOCK_FILE 255
00542 char sock_file[MAX_SOCK_FILE + 1];
00543 const char *home_dir = getenv("HOME");
00544 const char *kde_home = getenv("KDEHOME");
00545 char *display;
00546
00547 sock_file[0] = sock_file[MAX_SOCK_FILE] = 0;
00548
00549 if (!kde_home || !kde_home[0])
00550 {
00551 kde_home = "~/" KDE_DEFAULT_HOME "/";
00552 }
00553
00554 if (kde_home[0] == '~')
00555 {
00556 if (!home_dir || !home_dir[0])
00557 {
00558 fprintf(stderr, "Warning: $HOME not set!\n");
00559 return -1;
00560 }
00561 if (strlen(home_dir) > (MAX_SOCK_FILE-100))
00562 {
00563 fprintf(stderr, "Warning: Home directory path too long!\n");
00564 return -1;
00565 }
00566 kde_home++;
00567 strlcpy(sock_file, home_dir, MAX_SOCK_FILE);
00568 }
00569 strlcat(sock_file, kde_home, MAX_SOCK_FILE);
00570
00572 if ( sock_file[strlen(sock_file)-1] == '/')
00573 sock_file[strlen(sock_file)-1] = 0;
00574
00575 strlcat(sock_file, "/socket-", MAX_SOCK_FILE);
00576 if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0)
00577 {
00578 perror("Warning: Could not determine hostname: ");
00579 return -1;
00580 }
00581 sock_file[sizeof(sock_file)-1] = '\0';
00582
00583
00584 display = getDisplay();
00585 if (display == NULL)
00586 {
00587 fprintf(stderr, "Error: Could not determine display.\n");
00588 return -1;
00589 }
00590
00591 if (strlen(sock_file)+strlen(display)+strlen("/kdeinit4_")+2 > MAX_SOCK_FILE)
00592 {
00593 fprintf(stderr, "Warning: Socket name will be too long.\n");
00594 free(display);
00595 return -1;
00596 }
00597 strcat(sock_file, "/kdeinit4_");
00598 strcat(sock_file, display);
00599 free(display);
00600
00601 if (strlen(sock_file) >= sizeof(server.sun_path))
00602 {
00603 fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
00604 return -1;
00605 }
00606
00607
00608
00609
00610 s = socket(PF_UNIX, SOCK_STREAM, 0);
00611 if (s < 0)
00612 {
00613 perror("Warning: socket() failed: ");
00614 return -1;
00615 }
00616
00617 server.sun_family = AF_UNIX;
00618 strcpy(server.sun_path, sock_file);
00619 printf("sock_file=%s\n", sock_file);
00620 socklen = sizeof(server);
00621 if(connect(s, (struct sockaddr *)&server, socklen) == -1)
00622 {
00623 perror("Warning: connect() failed: ");
00624 close(s);
00625 return -1;
00626 }
00627 return s;
00628 }
00629
00630 #endif // Q_OS_UNIX