diff options
Diffstat (limited to 'indra/newview/llappviewerlinux.cpp')
| -rw-r--r-- | indra/newview/llappviewerlinux.cpp | 576 |
1 files changed, 338 insertions, 238 deletions
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index c36dd2955e..7629265730 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -1,31 +1,26 @@ /** * @file llappviewerlinux.cpp - * @brief The LLAppViewerWin32 class definitions + * @brief The LLAppViewerLinux class definitions * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,30 +31,41 @@ #include "llcommandlineparser.h" #include "llmemtype.h" +#include "llurldispatcher.h" // SLURL from other app instance #include "llviewernetwork.h" +#include "llviewercontrol.h" +#include "llwindowsdl.h" #include "llmd5.h" +#include "llfindlocale.h" - #if LL_LINUX - # include <dlfcn.h> // RTLD_LAZY - # include <execinfo.h> // backtrace - glibc only - # ifndef LL_ELFBIN - #define LL_ELFBIN 1 - # endif // LL_ELFBIN - # if LL_ELFBIN - # include <cxxabi.h> // for symbol demangling - # include "ELFIO.h" // for better backtraces - # endif // LL_ELFBIN - #elif LL_SOLARIS - # include <sys/types.h> - # include <unistd.h> - # include <fcntl.h> - # include <ucontext.h> - #endif +#include <exception> + +#if LL_DBUS_ENABLED +# include "llappviewerlinux_api_dbus.h" + +// regrettable hacks to give us better runtime compatibility with older systems inside llappviewerlinux_api.h: +#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0) +#undef g_return_if_fail +#define g_return_if_fail(COND) llg_return_if_fail(COND) +// The generated API +# include "llappviewerlinux_api.h" +#endif namespace { int gArgC = 0; char **gArgV = NULL; + void (*gOldTerminateHandler)() = NULL; +} + +static void exceptionTerminateHandler() +{ + // reinstall default terminate() handler in case we re-terminate. + if (gOldTerminateHandler) std::set_terminate(gOldTerminateHandler); + // treat this like a regular viewer crash, with nice stacktrace etc. + LLAppViewer::handleViewerCrash(); + // we've probably been killed-off before now, but... + gOldTerminateHandler(); // call old terminate() handler } int main( int argc, char **argv ) @@ -75,6 +81,9 @@ int main( int argc, char **argv ) LLAppViewer* viewer_app_ptr = new LLAppViewerLinux(); + // install unexpected exception handler + gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler); + // install crash handlers viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); bool ok = viewer_app_ptr->init(); @@ -104,263 +113,309 @@ int main( int argc, char **argv ) return 0; } -#ifdef LL_SOLARIS -static inline BOOL do_basic_glibc_backtrace() +LLAppViewerLinux::LLAppViewerLinux() { - BOOL success = FALSE; - - std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); - llinfos << "Opening stack trace file " << strace_filename << llendl; - FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); - if (!StraceFile) - { - llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; - StraceFile = stderr; - } - - printstack(fileno(StraceFile)); +} - if (StraceFile != stderr) - fclose(StraceFile); +LLAppViewerLinux::~LLAppViewerLinux() +{ +} - return success; +bool LLAppViewerLinux::init() +{ + // g_thread_init() must be called before *any* use of glib, *and* + // before any mutexes are held, *and* some of our third-party + // libraries likes to use glib functions; in short, do this here + // really early in app startup! + if (!g_thread_supported ()) g_thread_init (NULL); + + return LLAppViewer::init(); } -#else -#define MAX_STACK_TRACE_DEPTH 40 -// This uses glibc's basic built-in stack-trace functions for a not very -// amazing backtrace. -static inline BOOL do_basic_glibc_backtrace() + +bool LLAppViewerLinux::restoreErrorTrap() { - void *array[MAX_STACK_TRACE_DEPTH]; - size_t size; - char **strings; - size_t i; - BOOL success = FALSE; - - size = backtrace(array, MAX_STACK_TRACE_DEPTH); - strings = backtrace_symbols(array, size); - - std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); - llinfos << "Opening stack trace file " << strace_filename << llendl; - FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); // Flawfinder: ignore - if (!StraceFile) - { - llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; - StraceFile = stderr; - } + // *NOTE:Mani there is a case for implementing this on the mac. + // Linux doesn't need it to my knowledge. + return true; +} - if (size) - { - for (i = 0; i < size; i++) - fputs((std::string(strings[i])+"\n").c_str(), - StraceFile); +///////////////////////////////////////// +#if LL_DBUS_ENABLED - success = TRUE; - } - - if (StraceFile != stderr) - fclose(StraceFile); +typedef struct +{ + GObjectClass parent_class; +} ViewerAppAPIClass; + +static void viewerappapi_init(ViewerAppAPI *server); +static void viewerappapi_class_init(ViewerAppAPIClass *klass); + +/// + +// regrettable hacks to give us better runtime compatibility with older systems in general +static GType llg_type_register_static_simple_ONCE(GType parent_type, + const gchar *type_name, + guint class_size, + GClassInitFunc class_init, + guint instance_size, + GInstanceInitFunc instance_init, + GTypeFlags flags) +{ + static GTypeInfo type_info; + memset(&type_info, 0, sizeof(type_info)); - free (strings); - return success; + type_info.class_size = class_size; + type_info.class_init = class_init; + type_info.instance_size = instance_size; + type_info.instance_init = instance_init; + + return g_type_register_static(parent_type, type_name, &type_info, flags); } +#define llg_intern_static_string(S) (S) +#define g_intern_static_string(S) llg_intern_static_string(S) +#define g_type_register_static_simple(parent_type, type_name, class_size, class_init, instance_size, instance_init, flags) llg_type_register_static_simple_ONCE(parent_type, type_name, class_size, class_init, instance_size, instance_init, flags) -#if LL_ELFBIN -// This uses glibc's basic built-in stack-trace functions together with -// ELFIO's ability to parse the .symtab ELF section for better symbol -// extraction without exporting symbols (which'd cause subtle, fatal bugs). -static inline BOOL do_elfio_glibc_backtrace() -{ - void *array[MAX_STACK_TRACE_DEPTH]; - size_t btsize; - char **strings; - BOOL success = FALSE; +G_DEFINE_TYPE(ViewerAppAPI, viewerappapi, G_TYPE_OBJECT); - std::string appfilename = gDirUtilp->getExecutablePathAndName(); +void viewerappapi_class_init(ViewerAppAPIClass *klass) +{ +} - std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); - llinfos << "Opening stack trace file " << strace_filename << llendl; - FILE* StraceFile = LLFile::fopen(strace_filename.c_str(), "w"); // Flawfinder: ignore - if (!StraceFile) - { - llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl; - StraceFile = stderr; - } +static bool dbus_server_init = false; - // get backtrace address list and basic symbol info - btsize = backtrace(array, MAX_STACK_TRACE_DEPTH); - strings = backtrace_symbols(array, btsize); - - // create ELF reader for our app binary - IELFI* pReader; - const IELFISection* pSec = NULL; - IELFISymbolTable* pSymTbl = 0; - if (ERR_ELFIO_NO_ERROR != ELFIO::GetInstance()->CreateELFI(&pReader) || - ERR_ELFIO_NO_ERROR != pReader->Load(appfilename.c_str()) || - // find symbol table, create reader-object - NULL == (pSec = pReader->GetSection( ".symtab" )) || - ERR_ELFIO_NO_ERROR != pReader->CreateSectionReader(IELFI::ELFI_SYMBOL, pSec, (void**)&pSymTbl) ) - { - // Failed to open our binary and read its symbol table somehow - llinfos << "Could not initialize ELF symbol reading - doing basic backtrace." << llendl; - if (StraceFile != stderr) - fclose(StraceFile); - // note that we may be leaking some of the above ELFIO - // objects now, but it's expected that we'll be dead soon - // and we want to tread delicately until we get *some* kind - // of useful backtrace. - return do_basic_glibc_backtrace(); - } +void viewerappapi_init(ViewerAppAPI *server) +{ + // Connect to the default DBUS, register our service/API. - // iterate over trace and symtab, looking for plausible symbols - std::string name; - Elf32_Addr value; - Elf32_Word ssize; - unsigned char bind; - unsigned char type; - Elf32_Half section; - int nSymNo = pSymTbl->GetSymbolNum(); - size_t btpos; - for (btpos = 0; btpos < btsize; ++btpos) + if (!dbus_server_init) { - fprintf(StraceFile, "%d:\t", btpos); - int symidx; - for (symidx = 0; symidx < nSymNo; ++symidx) + GError *error = NULL; + + server->connection = lldbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (server->connection) { - if (ERR_ELFIO_NO_ERROR == - pSymTbl->GetSymbol(symidx, name, value, ssize, - bind, type, section)) + lldbus_g_object_type_install_info(viewerappapi_get_type(), &dbus_glib_viewerapp_object_info); + + lldbus_g_connection_register_g_object(server->connection, VIEWERAPI_PATH, G_OBJECT(server)); + + DBusGProxy *serverproxy = lldbus_g_proxy_new_for_name(server->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + + guint request_name_ret_unused; + // akin to org_freedesktop_DBus_request_name + if (lldbus_g_proxy_call(serverproxy, "RequestName", &error, G_TYPE_STRING, VIEWERAPI_SERVICE, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &request_name_ret_unused, G_TYPE_INVALID)) { - // check if trace address within symbol range - if (uintptr_t(array[btpos]) >= value && - uintptr_t(array[btpos]) < value+ssize) - { - char *demangled_str = NULL; - int demangle_result = 1; - demangled_str = - abi::__cxa_demangle - (name.c_str(), NULL, NULL, - &demangle_result); - if (0 == demangle_result && - NULL != demangled_str) { - fprintf(StraceFile, - "ELF(%s", demangled_str); - free(demangled_str); - } - else // failed demangle; print it raw - { - fprintf(StraceFile, - "ELF(%s", name.c_str()); - } - // print offset from symbol start - fprintf(StraceFile, - "+0x%lx) [%p]\n", - uintptr_t(array[btpos]) - - value, - array[btpos]); - goto got_sym; // early escape - } + // total success. + dbus_server_init = true; } + else + { + llwarns << "Unable to register service name: " << error->message << llendl; + } + + g_object_unref(serverproxy); + } + else + { + g_warning("Unable to connect to dbus: %s", error->message); } - // Fallback: - // Didn't find a suitable symbol in the binary - it's probably - // a symbol in a DSO; use glibc's idea of what it should be. - fprintf(StraceFile, "%s\n", strings[btpos]); - got_sym:; + + if (error) + g_error_free(error); } - - if (StraceFile != stderr) - fclose(StraceFile); +} - pSymTbl->Release(); - pSec->Release(); - pReader->Release(); +gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error) +{ + bool success = false; - free(strings); + llinfos << "Was asked to go to slurl: " << slurl << llendl; - llinfos << "Finished generating stack trace." << llendl; + std::string url = slurl; + LLMediaCtrl* web = NULL; + const bool trusted_browser = false; + if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + { + // bring window to foreground, as it has just been "launched" from a URL + // todo: hmm, how to get there from here? + //xxx->mWindow->bringToFront(); + success = true; + } - success = TRUE; - return success; -} -#endif // LL_ELFBIN + *success_rtn = g_new (gboolean, 1); + (*success_rtn)[0] = (gboolean)success; -#endif // LL_SOLARIS + return TRUE; // the invokation succeeded, even if the actual dispatch didn't. +} +/// -LLAppViewerLinux::LLAppViewerLinux() +//virtual +bool LLAppViewerLinux::initSLURLHandler() { + if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) + { + return false; // failed + } + + g_type_init(); + + //ViewerAppAPI *api_server = (ViewerAppAPI*) + g_object_new(viewerappapi_get_type(), NULL); + + return true; } -LLAppViewerLinux::~LLAppViewerLinux() +//virtual +bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) { + if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) + { + return false; // failed + } + + bool success = false; + DBusGConnection *bus; + GError *error = NULL; + + g_type_init(); + + bus = lldbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (bus) + { + gboolean rtn = FALSE; + DBusGProxy *remote_object = + lldbus_g_proxy_new_for_name(bus, VIEWERAPI_SERVICE, VIEWERAPI_PATH, VIEWERAPI_INTERFACE); + + if (lldbus_g_proxy_call(remote_object, "GoSLURL", &error, + G_TYPE_STRING, url.c_str(), G_TYPE_INVALID, + G_TYPE_BOOLEAN, &rtn, G_TYPE_INVALID)) + { + success = rtn; + } + else + { + llinfos << "Call-out to other instance failed (perhaps not running): " << error->message << llendl; + } + + g_object_unref(G_OBJECT(remote_object)); + } + else + { + llwarns << "Couldn't connect to session bus: " << error->message << llendl; + } + + if (error) + g_error_free(error); + + return success; } -bool LLAppViewerLinux::init() +#else // LL_DBUS_ENABLED +bool LLAppViewerLinux::initSLURLHandler() { - return LLAppViewer::init(); + return false; // not implemented without dbus +} +bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) +{ + return false; // not implemented without dbus } +#endif // LL_DBUS_ENABLED -void LLAppViewerLinux::handleCrashReporting() +void LLAppViewerLinux::handleCrashReporting(bool reportFreeze) { + std::string cmd =gDirUtilp->getExecutableDir(); + cmd += gDirUtilp->getDirDelimiter(); +#if LL_LINUX + cmd += "linux-crash-logger.bin"; +#elif LL_SOLARIS + cmd += "solaris-crash-logger"; +#else +# error Unknown platform +#endif - // Always generate the report, have the logger do the asking, and - // don't wait for the logger before exiting (-> total cleanup). - if (CRASH_BEHAVIOR_NEVER_SEND != LLAppViewer::instance()->getCrashBehavior()) - { - // This backtrace writes into stack_trace.log -# if LL_ELFBIN - do_elfio_glibc_backtrace(); // more useful backtrace -# else - do_basic_glibc_backtrace(); // only slightly useful backtrace -# endif // LL_ELFBIN - // launch the actual crash logger - char* ask_dialog = "-dialog"; - if (CRASH_BEHAVIOR_ASK != LLAppViewer::instance()->getCrashBehavior()) - ask_dialog = ""; // omit '-dialog' option - std::string cmd =gDirUtilp->getAppRODataDir(); - cmd += gDirUtilp->getDirDelimiter(); - cmd += "linux-crash-logger.bin"; + if(reportFreeze) + { char* const cmdargv[] = {(char*)cmd.c_str(), - ask_dialog, - (char*)"-user", - (char*)gGridName.c_str(), - (char*)"-name", - (char*)LLAppViewer::instance()->getSecondLifeTitle().c_str(), + (char*)"-previous", NULL}; + + fflush(NULL); // flush all buffers before the child inherits them pid_t pid = fork(); if (pid == 0) { // child - execv(cmd.c_str(), cmdargv); /* Flawfinder: ignore */ + execv(cmd.c_str(), cmdargv); /* Flawfinder: Ignore */ llwarns << "execv failure when trying to start " << cmd << llendl; _exit(1); // avoid atexit() - } - else - { + } else { if (pid > 0) { - // DO NOT wait for child proc to die; we want - // the logger to outlive us while we quit to - // free up the screen/keyboard/etc. - ////int childExitStatus; - ////waitpid(pid, &childExitStatus, 0); + // wait for child proc to die + int childExitStatus; + waitpid(pid, &childExitStatus, 0); + } else { + llwarns << "fork failure." << llendl; + } + } + } + else + { + const S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); + + // Always generate the report, have the logger do the asking, and + // don't wait for the logger before exiting (-> total cleanup). + if (CRASH_BEHAVIOR_NEVER_SEND != cb) + { + // launch the actual crash logger + const char* ask_dialog = "-dialog"; + if (CRASH_BEHAVIOR_ASK != cb) + ask_dialog = ""; // omit '-dialog' option + const char * cmdargv[] = + {cmd.c_str(), + ask_dialog, + "-user", + (char*)LLGridManager::getInstance()->getGridLabel().c_str(), + "-name", + LLAppViewer::instance()->getSecondLifeTitle().c_str(), + NULL}; + fflush(NULL); + pid_t pid = fork(); + if (pid == 0) + { // child + execv(cmd.c_str(), (char* const*) cmdargv); /* Flawfinder: ignore */ + llwarns << "execv failure when trying to start " << cmd << llendl; + _exit(1); // avoid atexit() } else { - llwarns << "fork failure." << llendl; + if (pid > 0) + { + // DO NOT wait for child proc to die; we want + // the logger to outlive us while we quit to + // free up the screen/keyboard/etc. + ////int childExitStatus; + ////waitpid(pid, &childExitStatus, 0); + } + else + { + llwarns << "fork failure." << llendl; + } } } + // Sometimes signals don't seem to quit the viewer. Also, we may + // have been called explicitly instead of from a signal handler. + // Make sure we exit so as to not totally confuse the user. + _exit(1); // avoid atexit(), else we may re-crash in dtors. } - // Sometimes signals don't seem to quit the viewer. - // Make sure we exit so as to not totally confuse the user. - exit(1); } bool LLAppViewerLinux::beingDebugged() { static enum {unknown, no, yes} debugged = unknown; - + +#if LL_SOLARIS + return debugged == no; // BUG: fix this for Solaris +#else if (debugged == unknown) { pid_t ppid = getppid(); @@ -395,21 +450,48 @@ bool LLAppViewerLinux::beingDebugged() } return debugged == yes; +#endif } bool LLAppViewerLinux::initLogging() { // Remove the last stack trace, if any + // This file is no longer created, since the move to Google Breakpad + // The code is left here to clean out any old state in the log dir std::string old_stack_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); - LLFile::remove(old_stack_file.c_str()); + LLFile::remove(old_stack_file); return LLAppViewer::initLogging(); } bool LLAppViewerLinux::initParseCommandLine(LLCommandLineParser& clp) { - clp.parseCommandLine(gArgC, gArgV); + if (!clp.parseCommandLine(gArgC, gArgV)) + { + return false; + } + + // Find the system language. + FL_Locale *locale = NULL; + FL_Success success = FL_FindLocale(&locale, FL_MESSAGES); + if (success != 0) + { + if (success >= 2 && locale->lang) // confident! + { + LL_INFOS("AppInit") << "Language " << ll_safe_string(locale->lang) << LL_ENDL; + LL_INFOS("AppInit") << "Location " << ll_safe_string(locale->country) << LL_ENDL; + LL_INFOS("AppInit") << "Variant " << ll_safe_string(locale->variant) << LL_ENDL; + + LLControlVariable* c = gSavedSettings.getControl("SystemLanguage"); + if(c) + { + c->setValue(std::string(locale->lang), false); + } + } + } + FL_FreeLocale(&locale); + return true; } @@ -417,8 +499,26 @@ std::string LLAppViewerLinux::generateSerialNumber() { char serial_md5[MD5HEX_STR_SIZE]; serial_md5[0] = 0; + std::string best; + std::string uuiddir("/dev/disk/by-uuid/"); + + // trawl /dev/disk/by-uuid looking for a good-looking UUID to grab + std::string this_name; + BOOL wrap = FALSE; + while (gDirUtilp->getNextFileInDir(uuiddir, "*", this_name, wrap)) + { + if (this_name.length() > best.length() || + (this_name.length() == best.length() && + this_name > best)) + { + // longest (and secondarily alphabetically last) so far + best = this_name; + } + } - // TODO + // we don't return the actual serial number, just a hash of it. + LLMD5 md5( reinterpret_cast<const unsigned char*>(best.c_str()) ); + md5.hex_digest(serial_md5); return serial_md5; } |
