Vidalia 0.2.10
|
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.vidalia-project.net/. No part of Vidalia, including this file, 00007 ** may be copied, modified, propagated, or distributed except according to the 00008 ** 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 ** \version $Id$ 00043 ** \brief General routines to install a Breakpad-based exception handler and 00044 ** set options related to launching the crash reporting application. 00045 */ 00046 00047 #include "CrashReporter.h" 00048 #include "stringutil.h" 00049 00050 #if defined(Q_OS_WIN32) 00051 #include <client/windows/handler/exception_handler.h> 00052 #elif defined(Q_OS_MAC) 00053 #include <client/mac/handler/exception_handler.h> 00054 #elif defined(Q_OS_LINUX) 00055 #include <client/linux/handler/exception_handler.h> 00056 #elif defined(Q_OS_SOLARIS) 00057 #include <client/solaris/handler/exception_handler.h> 00058 #endif 00059 00060 #include <QString> 00061 #include <QStringList> 00062 #include <QFileInfo> 00063 #include <QDir> 00064 00065 #include <time.h> 00066 00067 00068 namespace CrashReporter 00069 { 00070 #if defined(Q_OS_WIN32) 00071 typedef wchar_t _char_t; 00072 typedef HANDLE _file_handle_t; 00073 #define PATH_SEPARATOR TEXT("\\") 00074 # ifdef _USE_32BIT_TIME_T 00075 # define TIME_TO_STRING(buf, buflen, t) \ 00076 _ltoa_s(t, buf, buflen, 10) 00077 # else 00078 # define TIME_TO_STRING(buf, buflen, t) \ 00079 _i64toa_s(t, buf, buflen, 10) 00080 # endif 00081 #else 00082 typedef char _char_t; 00083 typedef int _file_handle_t; 00084 #define PATH_SEPARATOR "/" 00085 #define TEXT(x) (x) 00086 #define TIME_TO_STRING(buf, buflen, t) \ 00087 snprintf(buf, buflen, "%ld", t) 00088 #endif 00089 00090 /** Pointer to the Breakpad-installed exception handler called if Vidalia 00091 * crashes. 00092 * \sa install_exception_handler() 00093 */ 00094 static google_breakpad::ExceptionHandler *exceptionHandler = 0; 00095 00096 /** If true, the crash reporting application will be displayed when the 00097 * Breakpad-installed exception handler is called. Otherwise, the system 00098 * will handle the exception itself. 00099 * \sa set_crash_reporter() 00100 */ 00101 static bool showCrashReporter = false; 00102 00103 /** Absolute path of the crash reporting application that will be launched 00104 * from the exception handler. 00105 * \sa set_crash_reporter() 00106 */ 00107 static _char_t crashReporterExecutable[MAX_PATH_LEN + 1] = TEXT(""); 00108 00109 /** Version information for the application being monitored for crashes. 00110 * The version will be written to the extra information file alongside 00111 * the minidump. 00112 */ 00113 static char buildVersion[MAX_VERSION_LEN + 1] = ""; 00114 00115 /** Path and filename of the application to restart after displaying 00116 * the crash reporting dialog. The contents of this string are encoded 00117 * in UTF-8. 00118 * \sa set_restart_options() 00119 */ 00120 static char restartExecutable[MAX_CMD_LEN + 1] = ""; 00121 00122 /** Additional arguments to use when restarting the crashed application. 00123 * The contents of this string are encoded in UTF-8. 00124 * \sa set_restart_options() 00125 */ 00126 static char restartExecutableArgs[MAX_CMD_LEN + 1] = ""; 00127 00128 00129 /** Records the time at which install_exception_handler() is called, which 00130 * is usually as early as possible during application startup. This is used 00131 * in minidump_callback() to determine how long the application was running 00132 * before it crashed. 00133 * \sa install_exception_handler() 00134 * \sa minidump_callback() 00135 */ 00136 static time_t startupTime = 0; 00137 00138 00139 /** Slightly modified version of the strlcat() implementation by Todd C. 00140 * Miller (see the top of this file or the LICENSE file for license details), 00141 * that supports arguments of either wchar_t* on Windows or the usual char* 00142 * everywhere else but retains the semantics of strlcat(). 00143 */ 00144 static size_t 00145 append_string(_char_t *dst, const _char_t *src, size_t siz) 00146 { 00147 _char_t *d = dst; 00148 const _char_t *s = src; 00149 size_t n = siz; 00150 size_t dlen; 00151 00152 /* Find the end of dst and adjust bytes left but don't go past end */ 00153 while (n-- != 0 && *d != TEXT('\0')) 00154 d++; 00155 dlen = d - dst; 00156 n = siz - dlen; 00157 00158 if (n == 0) 00159 #if defined(Q_OS_WIN32) 00160 return (dlen + wcslen(s)); 00161 #else 00162 return(dlen + strlen(s)); 00163 #endif 00164 00165 while (*s != TEXT('\0')) { 00166 if (n != 1) { 00167 *d++ = *s; 00168 n--; 00169 } 00170 s++; 00171 } 00172 *d = TEXT('\0'); 00173 00174 return(dlen + (s - src)); /* count does not include NUL */ 00175 } 00176 00177 /** Writes the formatted string "<b>key</b>=</b>val\n" to the file handle 00178 * specified by <b>hFile</b>. On Windows, <b>hFile</b> is a HANDLE. Everywhere 00179 * else, <b>hFile</b> is an int. 00180 */ 00181 static void 00182 write_keyval_to_file(_file_handle_t hFile, const char *key, const char *val) 00183 { 00184 #if defined(Q_OS_WIN32) 00185 DWORD dwWritten; 00186 WriteFile(hFile, key, strlen(key), &dwWritten, NULL); 00187 WriteFile(hFile, "=", 1, &dwWritten, NULL); 00188 WriteFile(hFile, val, strlen(val), &dwWritten, NULL); 00189 WriteFile(hFile, "\n", 1, &dwWritten, NULL); 00190 #else 00191 write(hFile, key, strlen(key)); 00192 write(hFile, "=", 1); 00193 write(hFile, val, strlen(val)); 00194 write(hFile, "\n", 1); 00195 #endif 00196 } 00197 00198 /** Writes to a file extra information used by the crash reporting 00199 * application such as how long the application was running before it 00200 * crashed, the application to restart, as well as any extra arguments. 00201 * The contents of the file are formatted as a series of "Key=Val\n" pairs. 00202 * The written file has the same path and base filename as the minidump 00203 * file, with ".info" appended to the end. Returns true if the file was 00204 * created succesfully. Otherwise, returns false. 00205 */ 00206 static bool 00207 write_extra_dump_info(const _char_t *path, const _char_t *id, time_t crashTime) 00208 { 00209 static const char *KeyBuildVersion = "BuildVersion"; 00210 static const char *KeyCrashTime = "CrashTime"; 00211 static const char *KeyStartupTime = "StartupTime"; 00212 static const char *KeyRestartExecutable = "RestartExecutable"; 00213 static const char *KeyRestartExecutableArgs = "RestartExecutableArgs"; 00214 00215 _char_t extraInfoPath[MAX_PATH_LEN] = TEXT(""); 00216 append_string(extraInfoPath, path, MAX_PATH_LEN); 00217 append_string(extraInfoPath, PATH_SEPARATOR, MAX_PATH_LEN); 00218 append_string(extraInfoPath, id, MAX_PATH_LEN); 00219 size_t len = append_string(extraInfoPath, TEXT(".dmp.info"), MAX_PATH_LEN); 00220 if (len >= MAX_PATH_LEN) 00221 return false; 00222 00223 #if defined(Q_OS_WIN32) 00224 HANDLE hFile = CreateFile(extraInfoPath, GENERIC_WRITE, 0, NULL, 00225 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 00226 if (hFile == INVALID_HANDLE_VALUE) 00227 return false; 00228 #else 00229 /* TODO: Implement for non-Windowses */ 00230 #endif 00231 00232 char crashTimeString[24], startupTimeString[24]; 00233 TIME_TO_STRING(crashTimeString, 24, crashTime); 00234 TIME_TO_STRING(startupTimeString, 24, startupTime); 00235 00236 write_keyval_to_file(hFile, KeyBuildVersion, buildVersion); 00237 write_keyval_to_file(hFile, KeyCrashTime, crashTimeString); 00238 write_keyval_to_file(hFile, KeyStartupTime, startupTimeString); 00239 write_keyval_to_file(hFile, KeyRestartExecutable, restartExecutable); 00240 write_keyval_to_file(hFile, KeyRestartExecutableArgs, restartExecutableArgs); 00241 00242 #if defined(Q_OS_WIN32) 00243 CloseHandle(hFile); 00244 #else 00245 /* TODO: Implement for non-Windowses */ 00246 /* close(hFile); */ 00247 #endif 00248 return true; 00249 } 00250 00251 /** Breakpad-installed exception handler. This function gets called in the 00252 * event of a crash. If <b>showCrashReporter</b> is true, this will execute 00253 * the crash reporting application, passing it the name and location of the 00254 * generated minidump, the absolute path to the (now crashed) Vidalia 00255 * executable, and any arguments that may be needed to restart Vidalia. 00256 */ 00257 bool 00258 minidump_callback(const _char_t *path, // Path to the minidump file 00259 const _char_t *id, // Minidump UUID 00260 void *context, // Callback context 00261 #if defined(Q_OS_WIN32) 00262 EXCEPTION_POINTERS *exInfo, 00263 MDRawAssertionInfo *assertionInfo, 00264 #endif 00265 bool succeeded) 00266 { 00267 if (! succeeded || ! showCrashReporter) 00268 return false; 00269 00270 /* Write the extra dump info, such as application uptime, executable to 00271 * restart, and any necessary restart arguments. */ 00272 write_extra_dump_info(path, id, time(NULL)); 00273 00274 /* Format the command line used to launch the crash reporter */ 00275 _char_t commandLine[MAX_CMD_LEN] = TEXT(""); 00276 append_string(commandLine, TEXT("\""), MAX_CMD_LEN); 00277 append_string(commandLine, crashReporterExecutable, MAX_CMD_LEN); 00278 append_string(commandLine, TEXT("\" \""), MAX_CMD_LEN); 00279 append_string(commandLine, path, MAX_CMD_LEN); 00280 append_string(commandLine, PATH_SEPARATOR, MAX_CMD_LEN); 00281 append_string(commandLine, id, MAX_CMD_LEN); 00282 size_t len = append_string(commandLine, TEXT(".dmp\""), MAX_CMD_LEN); 00283 if (len >= MAX_CMD_LEN) 00284 return false; 00285 00286 /* Launch the crash reporter with the name and location of the minidump */ 00287 #if defined(Q_OS_WIN32) 00288 PROCESS_INFORMATION pi; 00289 STARTUPINFOW si; 00290 00291 ZeroMemory(&pi, sizeof(pi)); 00292 ZeroMemory(&si, sizeof(si)); 00293 si.cb = sizeof(si); 00294 si.dwFlags = STARTF_USESHOWWINDOW; 00295 si.wShowWindow = SW_SHOWDEFAULT; 00296 00297 BOOL rc = CreateProcess(NULL, (LPWSTR)commandLine, NULL, NULL, FALSE, 0, 00298 NULL, NULL, &si, &pi); 00299 if (rc) { 00300 CloseHandle(pi.hThread); 00301 CloseHandle(pi.hProcess); 00302 } 00303 TerminateProcess(GetCurrentProcess(), 1); 00304 return true; 00305 #else 00306 /* TODO: Implement for non-Windowses */ 00307 return false; 00308 #endif 00309 } 00310 00311 bool 00312 install_exception_handler(const QString &dumpPath) 00313 { 00314 /* Create a directory for the crash dumps if it doesn't already exist */ 00315 QDir dumpDir(dumpPath); 00316 if (! dumpDir.exists() && ! dumpDir.mkdir(".")) 00317 return false; 00318 00319 /* Create the exception handler and specify where the dumps should go */ 00320 exceptionHandler = new google_breakpad::ExceptionHandler( 00321 #if defined(Q_OS_WIN32) 00322 dumpDir.absolutePath().toStdWString(), 00323 #else 00324 dumpDir.absolutePath().toStdString(), 00325 #endif 00326 NULL, 00327 minidump_callback, 00328 NULL, 00329 #if defined(Q_OS_WIN32) 00330 google_breakpad::ExceptionHandler::HANDLER_ALL); 00331 #else 00332 true); 00333 #endif 00334 if (! exceptionHandler) 00335 return false; 00336 00337 startupTime = time(NULL); 00338 return true; 00339 } 00340 00341 void 00342 remove_exception_handler(void) 00343 { 00344 if (exceptionHandler) { 00345 delete exceptionHandler; 00346 exceptionHandler = 0; 00347 } 00348 } 00349 00350 bool 00351 set_crash_reporter(const QString &crashReporter) 00352 { 00353 #if defined(Q_OS_WIN32) 00354 if (crashReporter.length() <= CrashReporter::MAX_PATH_LEN) { 00355 crashReporter.toWCharArray(crashReporterExecutable); 00356 crashReporterExecutable[crashReporter.length()] = L'\0'; 00357 #else 00358 QByteArray utf8 = crashReporter.toUtf8(); 00359 if (utf8.length() <= CrashReporter::MAX_PATH_LEN) { 00360 memcpy(crashReporterExecutable, utf8.constData(), utf8.length()); 00361 crashReporterExecutable[utf8.length()] = '\0'; 00362 #endif 00363 showCrashReporter = true; 00364 } else { 00365 /* If the given path is longer than MAX_PATH_LEN, no crash reporting 00366 * application will be set since the user's platform wouldn't be able to 00367 * execute it anyway. 00368 */ 00369 showCrashReporter = false; 00370 } 00371 return showCrashReporter; 00372 } 00373 00374 bool 00375 set_restart_options(const QString &executable, const QStringList &arguments) 00376 { 00377 QByteArray exe = executable.toUtf8(); 00378 if (exe.length() > MAX_CMD_LEN) 00379 return false; 00380 00381 QByteArray args = string_format_arguments(arguments).toUtf8(); 00382 if (args.length() > MAX_CMD_LEN) 00383 return false; 00384 00385 memcpy(restartExecutable, exe.constData(), exe.length()); 00386 restartExecutable[exe.length()] = '\0'; 00387 00388 memcpy(restartExecutableArgs, args.constData(), args.length()); 00389 restartExecutableArgs[args.length()] = '\0'; 00390 00391 return true; 00392 } 00393 00394 bool 00395 set_build_version(const QString &version) 00396 { 00397 if (version.length() > MAX_VERSION_LEN) 00398 return false; 00399 00400 QByteArray ascii = version.toAscii(); 00401 memcpy(buildVersion, ascii.constData(), ascii.length()); 00402 buildVersion[ascii.length()] = '\0'; 00403 00404 return true; 00405 } 00406 00407 } 00408