This patch has no influence if you don't have libcwd installed. Note that libcwd (http://libcwd.sourceforge.net/) is only available for linux. A default compile of libcwd does memory allocation tracking, which is too slow for everyday usage of the viewer (usable, but notably slower) and we don't need that. Configure your libcwd as follows: ./configure --prefix=/sl/usr --disable-alloc --enable-optimize Or whatever prefix you prefer (add --enable-maintainer-mode if you're compiling it from the SVN repository), add --disable-nonthreading to compile twice as fast. If you have it installed you can activate it's use by setting a few environment variables: CXXFLAGS="$(pkg-config --cflags libcwd_r)" LDFLAGS="$(pkg-config --libs libcwd_r) -lpthread" and then reconfiguring the viewer. The -lpthread is needed when using ld.gold, however, if you leave it out you might get an LDFLAGS that ends on trailing whitespaces, which doesn't work for some reason. Also, if you installed it in a non-standard place (/sl/usr above) then you need this to run the viewer (and tests): export LD_LIBRARY_PATH="/sl/usr/lib"
762 lines
20 KiB
C++
762 lines
20 KiB
C++
/**
|
|
* @file llappviewerlinux.cpp
|
|
* @brief The LLAppViewerLinux class definitions
|
|
*
|
|
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2007-2009, Linden Research, Inc.
|
|
*
|
|
* 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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* 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.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llappviewerlinux.h"
|
|
|
|
#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"
|
|
|
|
#include <exception>
|
|
|
|
#if LL_LINUX
|
|
# include <dlfcn.h> // RTLD_LAZY
|
|
# include <execinfo.h> // backtrace - glibc only
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
# include <dirent.h>
|
|
# include <boost/algorithm/string.hpp>
|
|
#elif LL_SOLARIS
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
# include <fcntl.h>
|
|
# include <ucontext.h>
|
|
#endif
|
|
|
|
#ifdef LL_ELFBIN
|
|
# ifdef __GNUC__
|
|
# include <cxxabi.h> // for symbol demangling
|
|
# endif
|
|
# include "ELFIO/ELFIO.h" // for better backtraces
|
|
#endif
|
|
|
|
#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::handleSyncViewerCrash();
|
|
LLAppViewer::handleViewerCrash();
|
|
// we've probably been killed-off before now, but...
|
|
gOldTerminateHandler(); // call old terminate() handler
|
|
}
|
|
|
|
int main( int argc, char **argv )
|
|
{
|
|
Debug(debug::init());
|
|
Debug(libcw_do.on());
|
|
|
|
LLMemType mt1(LLMemType::MTYPE_STARTUP);
|
|
|
|
#if LL_SOLARIS && defined(__sparc)
|
|
asm ("ta\t6"); // NOTE: Make sure memory alignment is enforced on SPARC
|
|
#endif
|
|
|
|
gArgC = argc;
|
|
gArgV = 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);
|
|
viewer_app_ptr->setSyncErrorHandler(LLAppViewer::handleSyncViewerCrash);
|
|
|
|
bool ok = viewer_app_ptr->init();
|
|
if(!ok)
|
|
{
|
|
llwarns << "Application init failed." << llendl;
|
|
return -1;
|
|
}
|
|
|
|
// Run the application main loop
|
|
if(!LLApp::isQuitting())
|
|
{
|
|
viewer_app_ptr->mainLoop();
|
|
}
|
|
|
|
if (!LLApp::isError())
|
|
{
|
|
//
|
|
// We don't want to do cleanup here if the error handler got called -
|
|
// the assumption is that the error handler is responsible for doing
|
|
// app cleanup if there was a problem.
|
|
//
|
|
viewer_app_ptr->cleanup();
|
|
}
|
|
delete viewer_app_ptr;
|
|
viewer_app_ptr = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef LL_SOLARIS
|
|
static inline BOOL do_basic_glibc_backtrace()
|
|
{
|
|
BOOL success = FALSE;
|
|
|
|
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
|
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
|
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w");
|
|
if (!StraceFile)
|
|
{
|
|
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
|
StraceFile = stderr;
|
|
}
|
|
|
|
printstack(fileno(StraceFile));
|
|
|
|
if (StraceFile != stderr)
|
|
fclose(StraceFile);
|
|
|
|
return success;
|
|
}
|
|
#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()
|
|
{
|
|
void *stackarray[MAX_STACK_TRACE_DEPTH];
|
|
size_t size;
|
|
char **strings;
|
|
size_t i;
|
|
BOOL success = FALSE;
|
|
|
|
size = backtrace(stackarray, MAX_STACK_TRACE_DEPTH);
|
|
strings = backtrace_symbols(stackarray, size);
|
|
|
|
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
|
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
|
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w"); // Flawfinder: ignore
|
|
if (!StraceFile)
|
|
{
|
|
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
|
StraceFile = stderr;
|
|
}
|
|
|
|
if (size)
|
|
{
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
// the format of the StraceFile is very specific, to allow (kludgy) machine-parsing
|
|
fprintf(StraceFile, "%-3lu ", (unsigned long)i);
|
|
fprintf(StraceFile, "%-32s\t", "unknown");
|
|
fprintf(StraceFile, "%p ", stackarray[i]);
|
|
fprintf(StraceFile, "%s\n", strings[i]);
|
|
}
|
|
|
|
success = TRUE;
|
|
}
|
|
|
|
if (StraceFile != stderr)
|
|
fclose(StraceFile);
|
|
|
|
free (strings);
|
|
return success;
|
|
}
|
|
|
|
#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 *stackarray[MAX_STACK_TRACE_DEPTH];
|
|
size_t btsize;
|
|
char **strings;
|
|
BOOL success = FALSE;
|
|
|
|
std::string appfilename = gDirUtilp->getExecutablePathAndName();
|
|
|
|
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
|
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
|
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w"); // Flawfinder: ignore
|
|
if (!StraceFile)
|
|
{
|
|
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
|
StraceFile = stderr;
|
|
}
|
|
|
|
// get backtrace address list and basic symbol info
|
|
btsize = backtrace(stackarray, MAX_STACK_TRACE_DEPTH);
|
|
strings = backtrace_symbols(stackarray, 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();
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
// the format of the StraceFile is very specific, to allow (kludgy) machine-parsing
|
|
fprintf(StraceFile, "%-3ld ", (long)btpos);
|
|
int symidx;
|
|
for (symidx = 0; symidx < nSymNo; ++symidx)
|
|
{
|
|
if (ERR_ELFIO_NO_ERROR ==
|
|
pSymTbl->GetSymbol(symidx, name, value, ssize,
|
|
bind, type, section))
|
|
{
|
|
// check if trace address within symbol range
|
|
if (uintptr_t(stackarray[btpos]) >= value &&
|
|
uintptr_t(stackarray[btpos]) < value+ssize)
|
|
{
|
|
// symbol is inside viewer
|
|
fprintf(StraceFile, "%-32s\t", "com.secondlife.indra.viewer");
|
|
fprintf(StraceFile, "%p ", stackarray[btpos]);
|
|
|
|
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,
|
|
"%s", demangled_str);
|
|
free(demangled_str);
|
|
}
|
|
else // failed demangle; print it raw
|
|
{
|
|
fprintf(StraceFile,
|
|
"%s", name.c_str());
|
|
}
|
|
// print offset from symbol start
|
|
fprintf(StraceFile,
|
|
" + %lu\n",
|
|
uintptr_t(stackarray[btpos]) -
|
|
value);
|
|
goto got_sym; // early escape
|
|
}
|
|
}
|
|
}
|
|
// 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, "%-32s\t", "unknown");
|
|
fprintf(StraceFile, "%p ", stackarray[btpos]);
|
|
fprintf(StraceFile, "%s\n", strings[btpos]);
|
|
got_sym:;
|
|
}
|
|
|
|
if (StraceFile != stderr)
|
|
fclose(StraceFile);
|
|
|
|
pSymTbl->Release();
|
|
pSec->Release();
|
|
pReader->Release();
|
|
|
|
free(strings);
|
|
|
|
llinfos << "Finished generating stack trace." << llendl;
|
|
|
|
success = TRUE;
|
|
return success;
|
|
}
|
|
#endif // LL_ELFBIN
|
|
|
|
#endif // LL_SOLARIS
|
|
|
|
|
|
LLAppViewerLinux::LLAppViewerLinux()
|
|
{
|
|
}
|
|
|
|
LLAppViewerLinux::~LLAppViewerLinux()
|
|
{
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool LLAppViewerLinux::restoreErrorTrap()
|
|
{
|
|
// *NOTE:Mani there is a case for implementing this or the mac.
|
|
// Linux doesn't need it to my knowledge.
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
#if LL_DBUS_ENABLED
|
|
|
|
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));
|
|
|
|
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)
|
|
|
|
G_DEFINE_TYPE(ViewerAppAPI, viewerappapi, G_TYPE_OBJECT);
|
|
|
|
void viewerappapi_class_init(ViewerAppAPIClass *klass)
|
|
{
|
|
}
|
|
|
|
static bool dbus_server_init = false;
|
|
|
|
void viewerappapi_init(ViewerAppAPI *server)
|
|
{
|
|
// Connect to the default DBUS, register our service/API.
|
|
|
|
if (!dbus_server_init)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
server->connection = lldbus_g_bus_get(DBUS_BUS_SESSION, &error);
|
|
if (server->connection)
|
|
{
|
|
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))
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
if (error)
|
|
g_error_free(error);
|
|
}
|
|
}
|
|
|
|
gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error)
|
|
{
|
|
bool success = false;
|
|
|
|
llinfos << "Was asked to go to slurl: " << slurl << 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_rtn = g_new (gboolean, 1);
|
|
(*success_rtn)[0] = (gboolean)success;
|
|
|
|
return TRUE; // the invokation succeeded, even if the actual dispatch didn't.
|
|
}
|
|
|
|
///
|
|
|
|
//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;
|
|
}
|
|
|
|
//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;
|
|
}
|
|
|
|
#else // LL_DBUS_ENABLED
|
|
bool LLAppViewerLinux::initSLURLHandler()
|
|
{
|
|
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::handleSyncCrashTrace()
|
|
{
|
|
// 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
|
|
}
|
|
|
|
void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
|
|
{
|
|
std::string cmd =gDirUtilp->getAppRODataDir();
|
|
cmd += gDirUtilp->getDirDelimiter();
|
|
#if LL_LINUX
|
|
cmd += "linux-crash-logger.bin";
|
|
#elif LL_SOLARIS
|
|
cmd += "solaris-crash-logger";
|
|
#else
|
|
# error Unknown platform
|
|
#endif
|
|
|
|
if(reportFreeze)
|
|
{
|
|
char* const cmdargv[] =
|
|
{(char*)cmd.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 */
|
|
llwarns << "execv failure when trying to start " << cmd << llendl;
|
|
_exit(1); // avoid atexit()
|
|
} else {
|
|
if (pid > 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*)LLViewerLogin::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
|
|
{
|
|
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.
|
|
}
|
|
}
|
|
|
|
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();
|
|
char *name;
|
|
int ret;
|
|
|
|
ret = asprintf(&name, "/proc/%d/exe", ppid);
|
|
if (ret != -1)
|
|
{
|
|
char buf[1024];
|
|
ssize_t n;
|
|
|
|
n = readlink(name, buf, sizeof(buf) - 1);
|
|
if (n != -1)
|
|
{
|
|
char *base = strrchr(buf, '/');
|
|
buf[n + 1] = '\0';
|
|
if (base == NULL)
|
|
{
|
|
base = buf;
|
|
} else {
|
|
base += 1;
|
|
}
|
|
|
|
//should valgrind be added here?
|
|
if (strcmp(base, "gdb") == 0)
|
|
{
|
|
debugged = yes;
|
|
}
|
|
}
|
|
free(name);
|
|
}
|
|
}
|
|
|
|
return debugged == yes;
|
|
#endif
|
|
}
|
|
|
|
bool LLAppViewerLinux::initLogging()
|
|
{
|
|
// Remove the last stack trace, if any
|
|
std::string old_stack_file =
|
|
gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
|
LLFile::remove(old_stack_file);
|
|
|
|
return LLAppViewer::initLogging();
|
|
}
|
|
|
|
bool LLAppViewerLinux::initParseCommandLine(LLCommandLineParser& clp)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|