Vidalia
0.2.17
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 /* 00011 ** The append_string() function in this file is derived from the implementation 00012 ** of strlcat() by Todd C. Miller. It is licensed as follows: 00013 ** 00014 ** Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> 00015 ** All rights reserved. 00016 ** 00017 ** Redistribution and use in source and binary forms, with or without 00018 ** modification, are permitted provided that the following conditions 00019 ** are met: 00020 ** 1. Redistributions of source code must retain the above copyright 00021 ** notice, this list of conditions and the following disclaimer. 00022 ** 2. Redistributions in binary form must reproduce the above copyright 00023 ** notice, this list of conditions and the following disclaimer in the 00024 ** documentation and/or other materials provided with the distribution. 00025 ** 3. The name of the author may not be used to endorse or promote products 00026 ** derived from this software without specific prior written permission. 00027 ** 00028 ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 00029 ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 00030 ** AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 00031 ** THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00032 ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00033 ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 00034 ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 00035 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 00036 ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 00037 ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00038 */ 00039 00040 /* 00041 ** \file CrashReporter.h 00042 ** \brief General routines to install a Breakpad-based exception handler and 00043 ** set options related to launching the crash reporting application. 00044 */ 00045 00046 #include "CrashReporter.h" 00047 #include "stringutil.h" 00048 00049 #if defined(Q_OS_WIN32) 00050 #include <client/windows/handler/exception_handler.h> 00051 #elif defined(Q_OS_MAC) 00052 #include <client/mac/handler/exception_handler.h> 00053 #include <sys/wait.h> 00054 #include <fcntl.h> 00055 #elif defined(Q_OS_LINUX) 00056 #include <client/linux/handler/exception_handler.h> 00057 #include <sys/wait.h> 00058 #include <fcntl.h> 00059 #elif defined(Q_OS_SOLARIS) 00060 #include <client/solaris/handler/exception_handler.h> 00061 #endif 00062 00063 #include <QString> 00064 #include <QStringList> 00065 #include <QFileInfo> 00066 #include <QDir> 00067 00068 #include <time.h> 00069 00070 00071 namespace CrashReporter 00072 { 00073 #if defined(Q_OS_WIN32) 00074 typedef wchar_t _char_t; 00075 typedef HANDLE _file_handle_t; 00076 #define PATH_SEPARATOR TEXT("\\") 00077 # ifdef _USE_32BIT_TIME_T 00078 # define TIME_TO_STRING(buf, buflen, t) \ 00079 _ltoa_s(t, buf, buflen, 10) 00080 # else 00081 # define TIME_TO_STRING(buf, buflen, t) \ 00082 _i64toa_s(t, buf, buflen, 10) 00083 # endif 00084 #else 00085 typedef char _char_t; 00086 typedef int _file_handle_t; 00087 #define PATH_SEPARATOR "/" 00088 #define TEXT(x) (x) 00089 #define TIME_TO_STRING(buf, buflen, t) \ 00090 snprintf(buf, buflen, "%ld", t) 00091 #endif 00092 00093 /** Pointer to the Breakpad-installed exception handler called if Vidalia 00094 * crashes. 00095 * \sa install_exception_handler() 00096 */ 00097 static google_breakpad::ExceptionHandler *exceptionHandler = 0; 00098 00099 /** If true, the crash reporting application will be displayed when the 00100 * Breakpad-installed exception handler is called. Otherwise, the system 00101 * will handle the exception itself. 00102 * \sa set_crash_reporter() 00103 */ 00104 static bool showCrashReporter = false; 00105 00106 /** Absolute path of the crash reporting application that will be launched 00107 * from the exception handler. 00108 * \sa set_crash_reporter() 00109 */ 00110 static _char_t crashReporterExecutable[MAX_PATH_LEN + 1] = TEXT(""); 00111 00112 /** Version information for the application being monitored for crashes. 00113 * The version will be written to the extra information file alongside 00114 * the minidump. 00115 */ 00116 static char buildVersion[MAX_VERSION_LEN + 1] = ""; 00117 00118 /** Path and filename of the application to restart after displaying 00119 * the crash reporting dialog. The contents of this string are encoded 00120 * in UTF-8. 00121 * \sa set_restart_options() 00122 */ 00123 static char restartExecutable[MAX_CMD_LEN + 1] = ""; 00124 00125 /** Additional arguments to use when restarting the crashed application. 00126 * The contents of this string are encoded in UTF-8. 00127 * \sa set_restart_options() 00128 */ 00129 static char restartExecutableArgs[MAX_CMD_LEN + 1] = ""; 00130 00131 00132 /** Records the time at which install_exception_handler() is called, which 00133 * is usually as early as possible during application startup. This is used 00134 * in minidump_callback() to determine how long the application was running 00135 * before it crashed. 00136 * \sa install_exception_handler() 00137 * \sa minidump_callback() 00138 */ 00139 static time_t startupTime = 0; 00140 00141 00142 /** Slightly modified version of the strlcat() implementation by Todd C. 00143 * Miller (see the top of this file or the LICENSE file for license details), 00144 * that supports arguments of either wchar_t* on Windows or the usual char* 00145 * everywhere else but retains the semantics of strlcat(). 00146 */ 00147 static size_t 00148 append_string(_char_t *dst, const _char_t *src, size_t siz) 00149 { 00150 _char_t *d = dst; 00151 const _char_t *s = src; 00152 size_t n = siz; 00153 size_t dlen; 00154 00155 /* Find the end of dst and adjust bytes left but don't go past end */ 00156 while (n-- != 0 && *d != TEXT('\0')) 00157 d++; 00158 dlen = d - dst; 00159 n = siz - dlen; 00160 00161 if (n == 0) 00162 #if defined(Q_OS_WIN32) 00163 return (dlen + wcslen(s)); 00164 #else 00165 return(dlen + strlen(s)); 00166 #endif 00167 00168 while (*s != TEXT('\0')) { 00169 if (n != 1) { 00170 *d++ = *s; 00171 n--; 00172 } 00173 s++; 00174 } 00175 *d = TEXT('\0'); 00176 00177 return(dlen + (s - src)); /* count does not include NUL */ 00178 } 00179 00180 /** Writes the formatted string "<b>key</b>=</b>val\n" to the file handle 00181 * specified by <b>hFile</b>. On Windows, <b>hFile</b> is a HANDLE. Everywhere 00182 * else, <b>hFile</b> is an int. 00183 */ 00184 static void 00185 write_keyval_to_file(_file_handle_t hFile, const char *key, const char *val) 00186 { 00187 #if defined(Q_OS_WIN32) 00188 DWORD dwWritten; 00189 WriteFile(hFile, key, strlen(key), &dwWritten, NULL); 00190 WriteFile(hFile, "=", 1, &dwWritten, NULL); 00191 WriteFile(hFile, val, strlen(val), &dwWritten, NULL); 00192 WriteFile(hFile, "\n", 1, &dwWritten, NULL); 00193 #else 00194 write(hFile, key, strlen(key)); 00195 write(hFile, "=", 1); 00196 write(hFile, val, strlen(val)); 00197 write(hFile, "\n", 1); 00198 #endif 00199 } 00200 00201 /** Writes to a file extra information used by the crash reporting 00202 * application such as how long the application was running before it 00203 * crashed, the application to restart, as well as any extra arguments. 00204 * The contents of the file are formatted as a series of "Key=Val\n" pairs. 00205 * The written file has the same path and base filename as the minidump 00206 * file, with ".info" appended to the end. Returns true if the file was 00207 * created succesfully. Otherwise, returns false. 00208 */ 00209 static bool 00210 write_extra_dump_info(const _char_t *path, const _char_t *id, time_t crashTime) 00211 { 00212 static const char *KeyBuildVersion = "BuildVersion"; 00213 static const char *KeyCrashTime = "CrashTime"; 00214 static const char *KeyStartupTime = "StartupTime"; 00215 static const char *KeyRestartExecutable = "RestartExecutable"; 00216 static const char *KeyRestartExecutableArgs = "RestartExecutableArgs"; 00217 00218 _char_t extraInfoPath[MAX_PATH_LEN] = TEXT(""); 00219 append_string(extraInfoPath, path, MAX_PATH_LEN); 00220 append_string(extraInfoPath, PATH_SEPARATOR, MAX_PATH_LEN); 00221 append_string(extraInfoPath, id, MAX_PATH_LEN); 00222 size_t len = append_string(extraInfoPath, TEXT(".dmp.info"), MAX_PATH_LEN); 00223 if (len >= MAX_PATH_LEN) 00224 return false; 00225 00226 #if defined(Q_OS_WIN32) 00227 HANDLE hFile = CreateFile(extraInfoPath, GENERIC_WRITE, 0, NULL, 00228 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 00229 if (hFile == INVALID_HANDLE_VALUE) 00230 return false; 00231 #else 00232 _file_handle_t hFile = creat(extraInfoPath, S_IRUSR | S_IWUSR); 00233 if(hFile == -1) { 00234 return false; 00235 } 00236 #endif 00237 00238 char crashTimeString[24], startupTimeString[24]; 00239 TIME_TO_STRING(crashTimeString, 24, crashTime); 00240 TIME_TO_STRING(startupTimeString, 24, startupTime); 00241 00242 write_keyval_to_file(hFile, KeyBuildVersion, buildVersion); 00243 write_keyval_to_file(hFile, KeyCrashTime, crashTimeString); 00244 write_keyval_to_file(hFile, KeyStartupTime, startupTimeString); 00245 write_keyval_to_file(hFile, KeyRestartExecutable, restartExecutable); 00246 write_keyval_to_file(hFile, KeyRestartExecutableArgs, restartExecutableArgs); 00247 00248 #if defined(Q_OS_WIN32) 00249 CloseHandle(hFile); 00250 #else 00251 close(hFile); 00252 #endif 00253 return true; 00254 } 00255 00256 /** Breakpad-installed exception handler. This function gets called in the 00257 * event of a crash. If <b>showCrashReporter</b> is true, this will execute 00258 * the crash reporting application, passing it the name and location of the 00259 * generated minidump, the absolute path to the (now crashed) Vidalia 00260 * executable, and any arguments that may be needed to restart Vidalia. 00261 */ 00262 bool 00263 minidump_callback(const _char_t *path, // Path to the minidump file 00264 const _char_t *id, // Minidump UUID 00265 void *context, // Callback context 00266 #if defined(Q_OS_WIN32) 00267 EXCEPTION_POINTERS *exInfo, 00268 MDRawAssertionInfo *assertionInfo, 00269 #endif 00270 bool succeeded) 00271 { 00272 if (! succeeded || ! showCrashReporter) 00273 return false; 00274 00275 /* Write the extra dump info, such as application uptime, executable to 00276 * restart, and any necessary restart arguments. */ 00277 write_extra_dump_info(path, id, time(NULL)); 00278 00279 #if defined(Q_OS_WIN32) 00280 /* Format the command line used to launch the crash reporter */ 00281 _char_t commandLine[MAX_CMD_LEN] = TEXT(""); 00282 append_string(commandLine, TEXT("\""), MAX_CMD_LEN); 00283 append_string(commandLine, crashReporterExecutable, MAX_CMD_LEN); 00284 append_string(commandLine, TEXT("\" \""), MAX_CMD_LEN); 00285 append_string(commandLine, path, MAX_CMD_LEN); 00286 append_string(commandLine, PATH_SEPARATOR, MAX_CMD_LEN); 00287 append_string(commandLine, id, MAX_CMD_LEN); 00288 size_t len = append_string(commandLine, TEXT(".dmp\""), MAX_CMD_LEN); 00289 if (len >= MAX_CMD_LEN) 00290 return false; 00291 00292 /* Launch the crash reporter with the name and location of the minidump */ 00293 PROCESS_INFORMATION pi; 00294 STARTUPINFOW si; 00295 00296 ZeroMemory(&pi, sizeof(pi)); 00297 ZeroMemory(&si, sizeof(si)); 00298 si.cb = sizeof(si); 00299 si.dwFlags = STARTF_USESHOWWINDOW; 00300 si.wShowWindow = SW_SHOWDEFAULT; 00301 00302 BOOL rc = CreateProcess(NULL, (LPWSTR)commandLine, NULL, NULL, FALSE, 0, 00303 NULL, NULL, &si, &pi); 00304 if (rc) { 00305 CloseHandle(pi.hThread); 00306 CloseHandle(pi.hProcess); 00307 } 00308 TerminateProcess(GetCurrentProcess(), 1); 00309 return true; 00310 #else 00311 /* Format the command line used to launch the crash reporter */ 00312 _char_t args[MAX_CMD_LEN] = TEXT(""); 00313 size_t len; 00314 00315 append_string(args, path, MAX_CMD_LEN); 00316 append_string(args, PATH_SEPARATOR, MAX_CMD_LEN); 00317 append_string(args, id, MAX_CMD_LEN); 00318 len = append_string(args, TEXT(".dmp"), MAX_CMD_LEN); 00319 if (len >= MAX_CMD_LEN) 00320 return false; 00321 00322 char *nargs[] = {crashReporterExecutable, args, NULL}; 00323 00324 pid_t p = fork(), ret; 00325 int status; 00326 if(p == 0) { 00327 execv(crashReporterExecutable, nargs); 00328 } else { 00329 ret = wait(&status); 00330 if(ret == -1) 00331 return false; 00332 } 00333 return true; 00334 #endif 00335 } 00336 00337 bool 00338 install_exception_handler(const QString &dumpPath) 00339 { 00340 /* Create a directory for the crash dumps if it doesn't already exist */ 00341 QDir dumpDir(dumpPath); 00342 if (! dumpDir.exists() && ! dumpDir.mkdir(".")) 00343 return false; 00344 00345 /* Create the exception handler and specify where the dumps should go */ 00346 exceptionHandler = new google_breakpad::ExceptionHandler( 00347 #if defined(Q_OS_WIN32) 00348 dumpDir.absolutePath().toStdWString(), 00349 #else 00350 dumpDir.absolutePath().toStdString(), 00351 #endif 00352 NULL, 00353 minidump_callback, 00354 NULL, 00355 #if defined(Q_OS_WIN32) 00356 google_breakpad::ExceptionHandler::HANDLER_ALL); 00357 #else 00358 true); 00359 #endif 00360 if (! exceptionHandler) 00361 return false; 00362 00363 startupTime = time(NULL); 00364 return true; 00365 } 00366 00367 void 00368 remove_exception_handler(void) 00369 { 00370 if (exceptionHandler) { 00371 delete exceptionHandler; 00372 exceptionHandler = 0; 00373 } 00374 } 00375 00376 bool 00377 set_crash_reporter(const QString &crashReporter) 00378 { 00379 #if defined(Q_OS_WIN32) 00380 if (crashReporter.length() <= CrashReporter::MAX_PATH_LEN) { 00381 crashReporter.toWCharArray(crashReporterExecutable); 00382 crashReporterExecutable[crashReporter.length()] = L'\0'; 00383 #else 00384 QByteArray utf8 = crashReporter.toUtf8(); 00385 if (utf8.length() <= CrashReporter::MAX_PATH_LEN) { 00386 memcpy(crashReporterExecutable, utf8.constData(), utf8.length()); 00387 crashReporterExecutable[utf8.length()] = '\0'; 00388 #endif 00389 showCrashReporter = true; 00390 } else { 00391 /* If the given path is longer than MAX_PATH_LEN, no crash reporting 00392 * application will be set since the user's platform wouldn't be able to 00393 * execute it anyway. 00394 */ 00395 showCrashReporter = false; 00396 } 00397 return showCrashReporter; 00398 } 00399 00400 bool 00401 set_restart_options(const QString &executable, const QStringList &arguments) 00402 { 00403 QByteArray exe = executable.toUtf8(); 00404 if (exe.length() > MAX_CMD_LEN) 00405 return false; 00406 00407 QByteArray args = string_format_arguments(arguments).toUtf8(); 00408 if (args.length() > MAX_CMD_LEN) 00409 return false; 00410 00411 memcpy(restartExecutable, exe.constData(), exe.length()); 00412 restartExecutable[exe.length()] = '\0'; 00413 00414 memcpy(restartExecutableArgs, args.constData(), args.length()); 00415 restartExecutableArgs[args.length()] = '\0'; 00416 00417 return true; 00418 } 00419 00420 bool 00421 set_build_version(const QString &version) 00422 { 00423 if (version.length() > MAX_VERSION_LEN) 00424 return false; 00425 00426 QByteArray ascii = version.toAscii(); 00427 memcpy(buildVersion, ascii.constData(), ascii.length()); 00428 buildVersion[ascii.length()] = '\0'; 00429 00430 return true; 00431 } 00432 00433 } 00434