00001
00032 #include "linden_common.h"
00033
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <unistd.h>
00037
00038 #include <curl/curl.h>
00039
00040 #if LL_GTK
00041 # include "gtk/gtk.h"
00042 #endif // LL_GTK
00043
00044 #include "indra_constants.h"
00045 #include "llerror.h"
00046 #include "lltimer.h"
00047 #include "lldir.h"
00048
00049 #include "llstring.h"
00050
00051
00052
00053 static const char dialog_text[] =
00054 "Second Life appears to have crashed.\n"
00055 "This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, which are used for debugging purposes only.\n"
00056 "\n"
00057 "This is a third party version of the Second Life viewer provided by Dale Glass.\n"
00058 "It is unsupported by Linden Lab, and reports crashes to daleglass.net.\n"
00059 "Please don't contact Linden Lab for support, IM Dale Glass or send email to\n"
00060 "dale@daleglass.net instead."
00061 "\n"
00062 "Send crash report?";
00063
00064 static const char dialog_title[] =
00065 "Second Life Crash Logger";
00066
00067
00068 class LLFileEncoder
00069 {
00070 public:
00071 LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
00072
00073 BOOL isValid() const { return mIsValid; }
00074 LLString encodeURL(const S32 max_length = 0);
00075 public:
00076 BOOL mIsValid;
00077 LLString mFilename;
00078 LLString mFormname;
00079 LLString mBuf;
00080 };
00081
00082 LLString encode_string(const char *formname, const LLString &str);
00083
00084 LLString gServerResponse;
00085 BOOL gSendReport = FALSE;
00086 LLString gUserserver;
00087 LLString gUserText;
00088 BOOL gCrashInPreviousExec = FALSE;
00089 time_t gLaunchTime;
00090
00091 static size_t curl_download_callback(void *data, size_t size, size_t nmemb,
00092 void *user_data)
00093 {
00094 S32 bytes = size * nmemb;
00095 char *cdata = (char *) data;
00096 for (int i =0; i < bytes; i += 1)
00097 {
00098 gServerResponse += (cdata[i]);
00099 }
00100 return bytes;
00101 }
00102
00103 #if LL_GTK
00104 static void response_callback (GtkDialog *dialog,
00105 gint arg1,
00106 gpointer user_data)
00107 {
00108 gint *response = (gint*)user_data;
00109 *response = arg1;
00110 gtk_widget_destroy(GTK_WIDGET(dialog));
00111 gtk_main_quit();
00112 }
00113 #endif // LL_GTK
00114
00115 static BOOL do_ask_dialog(void)
00116 {
00117 #if LL_GTK
00118 gtk_disable_setlocale();
00119 if (!gtk_init_check(NULL, NULL)) {
00120 llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
00121 return FALSE;
00122 }
00123
00124 GtkWidget *win = NULL;
00125 GtkDialogFlags flags = GTK_DIALOG_MODAL;
00126 GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
00127 GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
00128 gint response = GTK_RESPONSE_NONE;
00129
00130 win = gtk_message_dialog_new(NULL,
00131 flags, messagetype, buttons,
00132 dialog_text);
00133 gtk_window_set_type_hint(GTK_WINDOW(win),
00134 GDK_WINDOW_TYPE_HINT_DIALOG);
00135 gtk_window_set_title(GTK_WINDOW(win), dialog_title);
00136 g_signal_connect (win,
00137 "response",
00138 G_CALLBACK (response_callback),
00139 &response);
00140 gtk_widget_show_all (win);
00141 gtk_main();
00142
00143 return (GTK_RESPONSE_OK == response ||
00144 GTK_RESPONSE_YES == response ||
00145 GTK_RESPONSE_APPLY == response);
00146 #else
00147 return FALSE;
00148 #endif // LL_GTK
00149 }
00150
00151
00152 int main(int argc, char **argv)
00153 {
00154 const S32 BT_MAX_SIZE = 100000;
00155 const S32 SL_MAX_SIZE = 100000;
00156 int i;
00157 S32 crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
00158
00159 time(&gLaunchTime);
00160
00161 llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
00162
00163 for(i=1; i<argc; i++)
00164 {
00165 if(!strcmp(argv[i], "-dialog"))
00166 {
00167 llinfos << "Show the user dialog" << llendl;
00168 crash_behavior = CRASH_BEHAVIOR_ASK;
00169 }
00170 if(!strcmp(argv[i], "-previous"))
00171 {
00172 gCrashInPreviousExec = TRUE;
00173 }
00174 if(!strcmp(argv[i], "-user"))
00175 {
00176 if ((i + 1) < argc)
00177 {
00178 i++;
00179 gUserserver = argv[i];
00180 llinfos << "Got userserver " << gUserserver << llendl;
00181 }
00182 }
00183 }
00184
00185 if( gCrashInPreviousExec )
00186 {
00187 llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
00188 }
00189
00190 if(!gCrashInPreviousExec)
00191 {
00192
00193
00194 sleep(5);
00195 }
00196
00197
00198 if (CRASH_BEHAVIOR_ALWAYS_SEND == crash_behavior)
00199 {
00200 gSendReport = TRUE;
00201 }
00202 else if (CRASH_BEHAVIOR_ASK == crash_behavior)
00203 {
00204 gSendReport = do_ask_dialog();
00205 }
00206
00207 if(!gSendReport)
00208 {
00209
00210 llinfos << "User cancelled, not sending report" << llendl;
00211
00212 return(0);
00213 }
00214
00215
00216 gDirUtilp->initAppDirs("SecondLife");
00217
00218
00219 LLString db_file_name;
00220 LLString sl_file_name;
00221 LLString bt_file_name;
00222 LLString st_file_name;
00223 LLString si_file_name;
00224
00225 LLFileEncoder *db_filep = NULL;
00226 LLFileEncoder *sl_filep = NULL;
00227 LLFileEncoder *st_filep = NULL;
00228 LLFileEncoder *bt_filep = NULL;
00229 LLFileEncoder *si_filep = NULL;
00230
00232
00233
00234
00235
00236
00237
00238 db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
00239 db_filep = new LLFileEncoder("DB", db_file_name.c_str());
00240
00241
00242
00243 char tmp_sl_name[LL_MAX_PATH];
00244 tmp_sl_name[0] = '\0';
00245 char tmp_space[256];
00246 tmp_space[0] = '\0';
00247
00248
00249 if (db_filep->isValid())
00250 {
00251
00252
00253 sscanf(db_filep->mBuf.c_str(), "SL Log:%255[ ]%1023[^\r\n]", tmp_space, tmp_sl_name);
00254 }
00255 else
00256 {
00257 delete db_filep;
00258 db_filep = NULL;
00259 }
00260
00261
00262 if (gCrashInPreviousExec)
00263 {
00264
00265
00266 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
00267 }
00268 else if (tmp_sl_name[0])
00269 {
00270 sl_file_name = tmp_sl_name;
00271 llinfos << "Using log file from debug log: " << sl_file_name << llendl;
00272 }
00273 else
00274 {
00275
00276 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
00277 }
00278
00279
00280 sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
00281 if (!sl_filep->isValid())
00282 {
00283 delete sl_filep;
00284 sl_filep = NULL;
00285 }
00286
00287 st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
00288 st_filep = new LLFileEncoder("ST", st_file_name.c_str());
00289 if (!st_filep->isValid())
00290 {
00291 delete st_filep;
00292 st_filep = NULL;
00293 }
00294
00295 si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml").c_str();
00296 si_filep = new LLFileEncoder("SI", si_file_name.c_str());
00297 if (!si_filep->isValid())
00298 {
00299 delete si_filep;
00300 si_filep = NULL;
00301 }
00302
00303
00304 bt_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
00305 bt_filep = new LLFileEncoder("DW", bt_file_name.c_str());
00306 if (!bt_filep->isValid())
00307 {
00308 delete bt_filep;
00309 bt_filep = NULL;
00310 }
00311
00312 LLString post_data;
00313 LLString tmp_url_buf;
00314
00315
00316 tmp_url_buf = encode_string("USER", gUserserver);
00317 post_data += tmp_url_buf;
00318 llinfos << "PostData:" << post_data << llendl;
00319
00320 if (gCrashInPreviousExec)
00321 {
00322 post_data.append("&");
00323 tmp_url_buf = encode_string("EF", "Y");
00324 post_data += tmp_url_buf;
00325 }
00326
00327 if (db_filep)
00328 {
00329 post_data.append("&");
00330 tmp_url_buf = db_filep->encodeURL();
00331 post_data += tmp_url_buf;
00332 llinfos << "Sending DB log file" << llendl;
00333 }
00334 else
00335 {
00336 llinfos << "Not sending DB log file" << llendl;
00337 }
00338
00339 if (sl_filep)
00340 {
00341 post_data.append("&");
00342 tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
00343 post_data += tmp_url_buf;
00344 llinfos << "Sending SL log file" << llendl;
00345 }
00346 else
00347 {
00348 llinfos << "Not sending SL log file" << llendl;
00349 }
00350
00351 if (st_filep)
00352 {
00353 post_data.append("&");
00354 tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
00355 post_data += tmp_url_buf;
00356 llinfos << "Sending stats log file" << llendl;
00357 }
00358 else
00359 {
00360 llinfos << "Not sending stats log file" << llendl;
00361 }
00362
00363 if (bt_filep)
00364 {
00365 post_data.append("&");
00366 tmp_url_buf = bt_filep->encodeURL(BT_MAX_SIZE);
00367 post_data += tmp_url_buf;
00368 llinfos << "Sending crash log file" << llendl;
00369 }
00370 else
00371 {
00372 llinfos << "Not sending crash log file" << llendl;
00373 }
00374
00375 if (si_filep)
00376 {
00377 post_data.append("&");
00378 tmp_url_buf = si_filep->encodeURL();
00379 post_data += tmp_url_buf;
00380 llinfos << "Sending settings log file" << llendl;
00381 }
00382 else
00383 {
00384 llinfos << "Not sending settings.xml file" << llendl;
00385 }
00386
00387 if (gUserText.size())
00388 {
00389 post_data.append("&");
00390 tmp_url_buf = encode_string("UN", gUserText);
00391 post_data += tmp_url_buf;
00392 }
00393
00394 delete db_filep;
00395 db_filep = NULL;
00396 delete sl_filep;
00397 sl_filep = NULL;
00398 delete bt_filep;
00399 bt_filep = NULL;
00400
00401
00402 #if 0
00403 printf("Crash report post data:\n--------\n");
00404 printf("%s", post_data.getString());
00405 printf("\n--------\n");
00406 #endif
00407
00408
00409 {
00410 CURL *curl = curl_easy_init();
00411
00412 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
00413 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
00414 curl_easy_setopt(curl, CURLOPT_POST, 1);
00415 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
00416 curl_easy_setopt(curl, CURLOPT_URL, "http://sl.daleglass.net/viewer_crash_reporter");
00417
00418 llinfos << "Connecting to crash report server" << llendl;
00419 CURLcode result = curl_easy_perform(curl);
00420
00421 curl_easy_cleanup(curl);
00422
00423 if(result != CURLE_OK)
00424 {
00425 llinfos << "Couldn't talk to crash report server" << llendl;
00426 }
00427 else
00428 {
00429 llinfos << "Response from crash report server:" << llendl;
00430 llinfos << gServerResponse << llendl;
00431 }
00432 }
00433
00434 return 0;
00435 }
00436
00437 LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
00438 {
00439 mFormname = form_name;
00440 mFilename = filename;
00441 mIsValid = FALSE;
00442
00443 int res;
00444
00445 struct stat stat_data;
00446 res = stat(mFilename.c_str(), &stat_data);
00447 if (res)
00448 {
00449 llwarns << "File " << mFilename << " is missing!" << llendl;
00450 return;
00451 }
00452 else
00453 {
00454
00455
00456
00457 if(!gCrashInPreviousExec && isCrashLog)
00458 {
00459
00460 double age = difftime(gLaunchTime, stat_data.st_mtim.tv_sec);
00461
00462
00463
00464 if(age > 60.0)
00465 {
00466
00467 llwarns << "File " << mFilename << " is too old!" << llendl;
00468 return;
00469 }
00470 }
00471
00472 }
00473
00474 S32 buf_size = stat_data.st_size;
00475 FILE *fp = fopen(mFilename.c_str(), "rb");
00476 U8 *buf = new U8[buf_size + 1];
00477 size_t nread = fread(buf, 1, buf_size, fp);
00478 fclose(fp);
00479 buf[nread] = 0;
00480
00481 mBuf = (char *)buf;
00482
00483 if(isCrashLog)
00484 {
00485
00486
00487
00488 const char *sep = "**********";
00489 const char *start = mBuf.c_str();
00490 const char *cur = start;
00491 const char *temp = strstr(cur, sep);
00492
00493 while(temp != NULL)
00494 {
00495
00496 cur = temp + strlen(sep);
00497
00498
00499 temp = strstr(cur, sep);
00500 }
00501
00502
00503 if(cur != start)
00504 {
00505 mBuf.erase(0, cur - start);
00506 }
00507 }
00508
00509 mIsValid = TRUE;
00510 delete[] buf;
00511 }
00512
00513 LLString LLFileEncoder::encodeURL(const S32 max_length)
00514 {
00515 LLString result = mFormname;
00516 result.append("=");
00517
00518 S32 i = 0;
00519
00520 if (max_length)
00521 {
00522 if ((S32)mBuf.size() > max_length)
00523 {
00524 i = mBuf.size() - max_length;
00525 }
00526 }
00527
00528 #if 0
00529
00530 result.append(mBuf);
00531 #else
00532
00533 S32 buf_size = mBuf.size();
00534 S32 url_buf_size = 3*mBuf.size() + 1;
00535 char *url_buf = new char[url_buf_size];
00536
00537 S32 cur_pos = 0;
00538 for (; i < buf_size; i++)
00539 {
00540 sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
00541 cur_pos += 3;
00542 }
00543 url_buf[i*3] = 0;
00544
00545 result.append(url_buf);
00546 delete[] url_buf;
00547 #endif
00548 return result;
00549 }
00550
00551 LLString encode_string(const char *formname, const LLString &str)
00552 {
00553 LLString result = formname;
00554 result.append("=");
00555
00556 S32 buf_size = str.size();
00557 S32 url_buf_size = 3*str.size() + 1;
00558 char *url_buf = new char[url_buf_size];
00559
00560 S32 cur_pos = 0;
00561 S32 i;
00562 for (i = 0; i < buf_size; i++)
00563 {
00564 sprintf(url_buf + cur_pos, "%%%02x", str[i]);
00565 cur_pos += 3;
00566 }
00567 url_buf[i*3] = 0;
00568
00569 result.append(url_buf);
00570 delete[] url_buf;
00571 return result;
00572 }