PageRenderTime 505ms CodeModel.GetById 151ms app.highlight 163ms RepoModel.GetById 139ms app.codeStats 0ms

/indra/newview/llappviewerlinux.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 515 lines | 362 code | 79 blank | 74 comment | 48 complexity | 844b87f36339d98918bd5ed122775dbb MD5 | raw file
  1/**
  2 * @file llappviewerlinux.cpp
  3 * @brief The LLAppViewerLinux class definitions
  4 *
  5 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */ 
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llappviewerlinux.h"
 30
 31#include "llcommandlineparser.h"
 32
 33#include "lldiriterator.h"
 34#include "llmemtype.h"
 35#include "llurldispatcher.h"		// SLURL from other app instance
 36#include "llviewernetwork.h"
 37#include "llviewercontrol.h"
 38#include "llwindowsdl.h"
 39#include "llmd5.h"
 40#include "llfindlocale.h"
 41
 42#include <exception>
 43
 44#if LL_DBUS_ENABLED
 45# include "llappviewerlinux_api_dbus.h"
 46
 47// regrettable hacks to give us better runtime compatibility with older systems inside llappviewerlinux_api.h:
 48#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0)
 49#undef g_return_if_fail
 50#define g_return_if_fail(COND) llg_return_if_fail(COND)
 51// The generated API
 52# include "llappviewerlinux_api.h"
 53#endif
 54
 55namespace
 56{
 57	int gArgC = 0;
 58	char **gArgV = NULL;
 59	void (*gOldTerminateHandler)() = NULL;
 60}
 61
 62static void exceptionTerminateHandler()
 63{
 64	// reinstall default terminate() handler in case we re-terminate.
 65	if (gOldTerminateHandler) std::set_terminate(gOldTerminateHandler);
 66	// treat this like a regular viewer crash, with nice stacktrace etc.
 67	LLAppViewer::handleViewerCrash();
 68	// we've probably been killed-off before now, but...
 69	gOldTerminateHandler(); // call old terminate() handler
 70}
 71
 72int main( int argc, char **argv ) 
 73{
 74	LLMemType mt1(LLMemType::MTYPE_STARTUP);
 75
 76#if LL_SOLARIS && defined(__sparc)
 77	asm ("ta\t6");		 // NOTE:  Make sure memory alignment is enforced on SPARC
 78#endif
 79
 80	gArgC = argc;
 81	gArgV = argv;
 82
 83	LLAppViewer* viewer_app_ptr = new LLAppViewerLinux();
 84
 85	// install unexpected exception handler
 86	gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);
 87	// install crash handlers
 88	viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
 89
 90	bool ok = viewer_app_ptr->init();
 91	if(!ok)
 92	{
 93		llwarns << "Application init failed." << llendl;
 94		return -1;
 95	}
 96
 97		// Run the application main loop
 98	if(!LLApp::isQuitting()) 
 99	{
100		viewer_app_ptr->mainLoop();
101	}
102
103	if (!LLApp::isError())
104	{
105		//
106		// We don't want to do cleanup here if the error handler got called -
107		// the assumption is that the error handler is responsible for doing
108		// app cleanup if there was a problem.
109		//
110		viewer_app_ptr->cleanup();
111	}
112	delete viewer_app_ptr;
113	viewer_app_ptr = NULL;
114	return 0;
115}
116
117LLAppViewerLinux::LLAppViewerLinux()
118{
119}
120
121LLAppViewerLinux::~LLAppViewerLinux()
122{
123}
124
125bool LLAppViewerLinux::init()
126{
127	// g_thread_init() must be called before *any* use of glib, *and*
128	// before any mutexes are held, *and* some of our third-party
129	// libraries likes to use glib functions; in short, do this here
130	// really early in app startup!
131	if (!g_thread_supported ()) g_thread_init (NULL);
132	
133	return LLAppViewer::init();
134}
135
136bool LLAppViewerLinux::restoreErrorTrap()
137{
138	// *NOTE:Mani there is a case for implementing this on the mac.
139	// Linux doesn't need it to my knowledge.
140	return true;
141}
142
143/////////////////////////////////////////
144#if LL_DBUS_ENABLED
145
146typedef struct
147{
148        GObjectClass parent_class;
149} ViewerAppAPIClass;
150
151static void viewerappapi_init(ViewerAppAPI *server);
152static void viewerappapi_class_init(ViewerAppAPIClass *klass);
153
154///
155
156// regrettable hacks to give us better runtime compatibility with older systems in general
157static GType llg_type_register_static_simple_ONCE(GType parent_type,
158						  const gchar *type_name,
159						  guint class_size,
160						  GClassInitFunc class_init,
161						  guint instance_size,
162						  GInstanceInitFunc instance_init,
163						  GTypeFlags flags)
164{
165	static GTypeInfo type_info;
166	memset(&type_info, 0, sizeof(type_info));
167
168	type_info.class_size = class_size;
169	type_info.class_init = class_init;
170	type_info.instance_size = instance_size;
171	type_info.instance_init = instance_init;
172
173	return g_type_register_static(parent_type, type_name, &type_info, flags);
174}
175#define llg_intern_static_string(S) (S)
176#define g_intern_static_string(S) llg_intern_static_string(S)
177#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)
178
179G_DEFINE_TYPE(ViewerAppAPI, viewerappapi, G_TYPE_OBJECT);
180
181void viewerappapi_class_init(ViewerAppAPIClass *klass)
182{
183}
184
185static bool dbus_server_init = false;
186
187void viewerappapi_init(ViewerAppAPI *server)
188{
189	// Connect to the default DBUS, register our service/API.
190
191	if (!dbus_server_init)
192	{
193		GError *error = NULL;
194		
195		server->connection = lldbus_g_bus_get(DBUS_BUS_SESSION, &error);
196		if (server->connection)
197		{
198			lldbus_g_object_type_install_info(viewerappapi_get_type(), &dbus_glib_viewerapp_object_info);
199			
200			lldbus_g_connection_register_g_object(server->connection, VIEWERAPI_PATH, G_OBJECT(server));
201			
202			DBusGProxy *serverproxy = lldbus_g_proxy_new_for_name(server->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
203
204			guint request_name_ret_unused;
205			// akin to org_freedesktop_DBus_request_name
206			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))
207			{
208				// total success.
209				dbus_server_init = true;
210			}
211			else 
212			{
213				llwarns << "Unable to register service name: " << error->message << llendl;
214			}
215	
216			g_object_unref(serverproxy);
217		}
218		else
219		{
220			g_warning("Unable to connect to dbus: %s", error->message);
221		}
222
223		if (error)
224			g_error_free(error);
225	}
226}
227
228gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error)
229{
230	bool success = false;
231
232	llinfos << "Was asked to go to slurl: " << slurl << llendl;
233
234	std::string url = slurl;
235	LLMediaCtrl* web = NULL;
236	const bool trusted_browser = false;
237	if (LLURLDispatcher::dispatch(url, "", web, trusted_browser))
238	{
239		// bring window to foreground, as it has just been "launched" from a URL
240		// todo: hmm, how to get there from here?
241		//xxx->mWindow->bringToFront();
242		success = true;
243	}		
244
245	*success_rtn = g_new (gboolean, 1);
246	(*success_rtn)[0] = (gboolean)success;
247
248	return TRUE; // the invokation succeeded, even if the actual dispatch didn't.
249}
250
251///
252
253//virtual
254bool LLAppViewerLinux::initSLURLHandler()
255{
256	if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME))
257	{
258		return false; // failed
259	}
260
261	g_type_init();
262
263	//ViewerAppAPI *api_server = (ViewerAppAPI*)
264	g_object_new(viewerappapi_get_type(), NULL);
265
266	return true;
267}
268
269//virtual
270bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url)
271{
272	if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME))
273	{
274		return false; // failed
275	}
276
277	bool success = false;
278	DBusGConnection *bus;
279	GError *error = NULL;
280
281	g_type_init();
282	
283	bus = lldbus_g_bus_get (DBUS_BUS_SESSION, &error);
284	if (bus)
285	{
286		gboolean rtn = FALSE;
287		DBusGProxy *remote_object =
288			lldbus_g_proxy_new_for_name(bus, VIEWERAPI_SERVICE, VIEWERAPI_PATH, VIEWERAPI_INTERFACE);
289
290		if (lldbus_g_proxy_call(remote_object, "GoSLURL", &error,
291					G_TYPE_STRING, url.c_str(), G_TYPE_INVALID,
292				       G_TYPE_BOOLEAN, &rtn, G_TYPE_INVALID))
293		{
294			success = rtn;
295		}
296		else
297		{
298			llinfos << "Call-out to other instance failed (perhaps not running): " << error->message << llendl;
299		}
300
301		g_object_unref(G_OBJECT(remote_object));
302	}
303	else
304	{
305		llwarns << "Couldn't connect to session bus: " << error->message << llendl;
306	}
307
308	if (error)
309		g_error_free(error);
310	
311	return success;
312}
313
314#else // LL_DBUS_ENABLED
315bool LLAppViewerLinux::initSLURLHandler()
316{
317	return false; // not implemented without dbus
318}
319bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url)
320{
321	return false; // not implemented without dbus
322}
323#endif // LL_DBUS_ENABLED
324
325void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
326{
327	std::string cmd =gDirUtilp->getExecutableDir();
328	cmd += gDirUtilp->getDirDelimiter();
329#if LL_LINUX
330	cmd += "linux-crash-logger.bin";
331#elif LL_SOLARIS
332	cmd += "solaris-crash-logger";
333#else
334# error Unknown platform
335#endif
336
337	if(reportFreeze)
338	{
339		char* const cmdargv[] =
340			{(char*)cmd.c_str(),
341			 (char*)"-previous",
342			 NULL};
343
344		fflush(NULL); // flush all buffers before the child inherits them
345		pid_t pid = fork();
346		if (pid == 0)
347		{ // child
348			execv(cmd.c_str(), cmdargv);		/* Flawfinder: Ignore */
349			llwarns << "execv failure when trying to start " << cmd << llendl;
350			_exit(1); // avoid atexit()
351		} else {
352			if (pid > 0)
353			{
354				// wait for child proc to die
355				int childExitStatus;
356				waitpid(pid, &childExitStatus, 0);
357			} else {
358				llwarns << "fork failure." << llendl;
359			}
360		}
361	}
362	else
363	{
364		// launch the actual crash logger
365		const char * cmdargv[] =
366			{cmd.c_str(),
367			 "-user",
368			 (char*)LLGridManager::getInstance()->getGridLabel().c_str(),
369			 "-name",
370			 LLAppViewer::instance()->getSecondLifeTitle().c_str(),
371			 NULL};
372		fflush(NULL);
373		pid_t pid = fork();
374		if (pid == 0)
375		{ // child
376			execv(cmd.c_str(), (char* const*) cmdargv);		/* Flawfinder: ignore */
377			llwarns << "execv failure when trying to start " << cmd << llendl;
378			_exit(1); // avoid atexit()
379		} 
380		else
381		{
382			if (pid > 0)
383			{
384				// DO NOT wait for child proc to die; we want
385				// the logger to outlive us while we quit to
386				// free up the screen/keyboard/etc.
387				////int childExitStatus;
388				////waitpid(pid, &childExitStatus, 0);
389			} 
390			else
391			{
392				llwarns << "fork failure." << llendl;
393			}
394		}
395		// Sometimes signals don't seem to quit the viewer.  Also, we may
396		// have been called explicitly instead of from a signal handler.
397		// Make sure we exit so as to not totally confuse the user.
398		_exit(1); // avoid atexit(), else we may re-crash in dtors.
399	}
400}
401
402bool LLAppViewerLinux::beingDebugged()
403{
404	static enum {unknown, no, yes} debugged = unknown;
405
406#if LL_SOLARIS
407	return debugged == no;	// BUG: fix this for Solaris
408#else
409	if (debugged == unknown)
410	{
411		pid_t ppid = getppid();
412		char *name;
413		int ret;
414
415		ret = asprintf(&name, "/proc/%d/exe", ppid);
416		if (ret != -1)
417		{
418			char buf[1024];
419			ssize_t n;
420			
421			n = readlink(name, buf, sizeof(buf) - 1);
422			if (n != -1)
423			{
424				char *base = strrchr(buf, '/');
425				buf[n + 1] = '\0';
426				if (base == NULL)
427				{
428					base = buf;
429				} else {
430					base += 1;
431				}
432				
433				if (strcmp(base, "gdb") == 0)
434				{
435					debugged = yes;
436				}
437			}
438			free(name);
439		}
440	}
441
442	return debugged == yes;
443#endif
444}
445
446bool LLAppViewerLinux::initLogging()
447{
448	// Remove the last stack trace, if any
449	// This file is no longer created, since the move to Google Breakpad
450	// The code is left here to clean out any old state in the log dir
451	std::string old_stack_file =
452		gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
453	LLFile::remove(old_stack_file);
454
455	return LLAppViewer::initLogging();
456}
457
458bool LLAppViewerLinux::initParseCommandLine(LLCommandLineParser& clp)
459{
460	if (!clp.parseCommandLine(gArgC, gArgV))
461	{
462		return false;
463	}
464
465	// Find the system language.
466	FL_Locale *locale = NULL;
467	FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
468	if (success != 0)
469	{
470		if (success >= 2 && locale->lang) // confident!
471		{
472			LL_INFOS("AppInit") << "Language " << ll_safe_string(locale->lang) << LL_ENDL;
473			LL_INFOS("AppInit") << "Location " << ll_safe_string(locale->country) << LL_ENDL;
474			LL_INFOS("AppInit") << "Variant " << ll_safe_string(locale->variant) << LL_ENDL;
475
476			LLControlVariable* c = gSavedSettings.getControl("SystemLanguage");
477			if(c)
478			{
479				c->setValue(std::string(locale->lang), false);
480			}
481		}
482	}
483	FL_FreeLocale(&locale);
484
485	return true;
486}
487
488std::string LLAppViewerLinux::generateSerialNumber()
489{
490	char serial_md5[MD5HEX_STR_SIZE];
491	serial_md5[0] = 0;
492	std::string best;
493	std::string uuiddir("/dev/disk/by-uuid/");
494
495	// trawl /dev/disk/by-uuid looking for a good-looking UUID to grab
496	std::string this_name;
497
498	LLDirIterator iter(uuiddir, "*");
499	while (iter.next(this_name))
500	{
501		if (this_name.length() > best.length() ||
502		    (this_name.length() == best.length() &&
503		     this_name > best))
504		{
505			// longest (and secondarily alphabetically last) so far
506			best = this_name;
507		}
508	}
509
510	// we don't return the actual serial number, just a hash of it.
511	LLMD5 md5( reinterpret_cast<const unsigned char*>(best.c_str()) );
512	md5.hex_digest(serial_md5);
513
514	return serial_md5;
515}