PageRenderTime 77ms CodeModel.GetById 25ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llplugin/slplugin/slplugin.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 400 lines | 247 code | 57 blank | 96 comment | 47 complexity | 0314d243385c7480c395da06c01f7513 MD5 | raw file
  1/**
  2 * @file slplugin.cpp
  3 * @brief Loader shell for plugins, intended to be launched by the plugin host application, which directly loads a plugin dynamic library.
  4 *
  5 * @cond
  6 *
  7 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 *
 28 * @endcond
 29 */
 30
 31
 32#include "linden_common.h"
 33
 34#include "llpluginprocesschild.h"
 35#include "llpluginmessage.h"
 36#include "llerrorcontrol.h"
 37#include "llapr.h"
 38#include "llstring.h"
 39
 40#if LL_DARWIN
 41	#include <Carbon/Carbon.h>
 42	#include "slplugin-objc.h"
 43#endif
 44
 45#if LL_DARWIN || LL_LINUX
 46	#include <signal.h>
 47#endif
 48
 49/*
 50	On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly or LSUIElement flag in the Info.plist.
 51
 52	Normally non-bundled binaries don't have an info.plist file, but it's possible to embed one in the binary by adding this to the linker flags:
 53
 54	-sectcreate __TEXT __info_plist /path/to/slplugin_info.plist
 55
 56	which means adding this to the gcc flags:
 57
 58	-Wl,-sectcreate,__TEXT,__info_plist,/path/to/slplugin_info.plist
 59	
 60	Now that SLPlugin is a bundled app on the Mac, this is no longer necessary (it can just use a regular Info.plist file), but I'm leaving this comment in for posterity.
 61*/
 62
 63#if LL_DARWIN || LL_LINUX
 64// Signal handlers to make crashes not show an OS dialog...
 65static void crash_handler(int sig)
 66{
 67	// Just exit cleanly.
 68	// TODO: add our own crash reporting
 69	_exit(1);
 70}
 71#endif
 72
 73#if LL_WINDOWS
 74#include <windows.h>
 75////////////////////////////////////////////////////////////////////////////////
 76//	Our exception handler - will probably just exit and the host application
 77//	will miss the heartbeat and log the error in the usual fashion.
 78LONG WINAPI myWin32ExceptionHandler( struct _EXCEPTION_POINTERS* exception_infop )
 79{
 80	//std::cerr << "This plugin (" << __FILE__ << ") - ";
 81	//std::cerr << "intercepted an unhandled exception and will exit immediately." << std::endl;
 82
 83	// TODO: replace exception handler before we exit?
 84	return EXCEPTION_EXECUTE_HANDLER;
 85}
 86
 87// Taken from : http://blog.kalmbachnet.de/?postid=75
 88// The MSVC 2005 CRT forces the call of the default-debugger (normally Dr.Watson)
 89// even with the other exception handling code. This (terrifying) piece of code
 90// patches things so that doesn't happen.
 91LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
 92	LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter )
 93{
 94	return NULL;
 95}
 96
 97BOOL PreventSetUnhandledExceptionFilter()
 98{
 99// WARNING: This won't work on 64-bit Windows systems so we turn it off it.
100//          It should work for any flavor of 32-bit Windows we care about.
101//          If it's off, sometimes you will see an OS message when a plugin crashes
102#ifndef _WIN64
103	HMODULE hKernel32 = LoadLibraryA( "kernel32.dll" );
104	if ( NULL == hKernel32 )
105		return FALSE;
106
107	void *pOrgEntry = GetProcAddress( hKernel32, "SetUnhandledExceptionFilter" );
108	if( NULL == pOrgEntry )
109		return FALSE;
110
111	unsigned char newJump[ 100 ];
112	DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
113	dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
114	void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
115	DWORD dwNewEntryAddr = (DWORD) pNewFunc;
116	DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
117
118	newJump[ 0 ] = 0xE9;  // JMP absolute
119	memcpy( &newJump[ 1 ], &dwRelativeAddr, sizeof( pNewFunc ) );
120	SIZE_T bytesWritten;
121	BOOL bRet = WriteProcessMemory( GetCurrentProcess(), pOrgEntry, newJump, sizeof( pNewFunc ) + 1, &bytesWritten );
122	return bRet;
123#else
124	return FALSE;
125#endif
126}
127
128////////////////////////////////////////////////////////////////////////////////
129//	Hook our exception handler and replace the system one
130void initExceptionHandler()
131{
132	LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
133
134	// save old exception handler in case we need to restore it at the end
135	prev_filter = SetUnhandledExceptionFilter( myWin32ExceptionHandler );
136	PreventSetUnhandledExceptionFilter();
137}
138
139bool checkExceptionHandler()
140{
141	bool ok = true;
142	LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
143	prev_filter = SetUnhandledExceptionFilter(myWin32ExceptionHandler);
144
145	PreventSetUnhandledExceptionFilter();
146
147	if (prev_filter != myWin32ExceptionHandler)
148	{
149		LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with " << prev_filter << "!" << LL_ENDL;
150		ok = false;
151	}
152
153	if (prev_filter == NULL)
154	{
155		ok = FALSE;
156		if (NULL == myWin32ExceptionHandler)
157		{
158			LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
159		}
160		else
161		{
162			LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with NULL!" << LL_ENDL;
163		}
164	}
165
166	return ok;
167}
168#endif
169
170// If this application on Windows platform is a console application, a console is always
171// created which is bad. Making it a Windows "application" via CMake settings but not
172// adding any code to explicitly create windows does the right thing.
173#if LL_WINDOWS
174int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
175#else
176int main(int argc, char **argv)
177#endif
178{
179	ll_init_apr();
180
181	// Set up llerror logging
182	{
183		LLError::initForApplication(".");
184		LLError::setDefaultLevel(LLError::LEVEL_INFO);
185//		LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG);
186//		LLError::logToFile("slplugin.log");
187	}
188
189#if LL_WINDOWS
190	if( strlen( lpCmdLine ) == 0 )
191	{
192		LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
193	};
194
195	U32 port = 0;
196	if(!LLStringUtil::convertToU32(lpCmdLine, port))
197	{
198		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
199	};
200
201	// Insert our exception handler into the system so this plugin doesn't
202	// display a crash message if something bad happens. The host app will
203	// see the missing heartbeat and log appropriately.
204	initExceptionHandler();
205#elif LL_DARWIN || LL_LINUX
206	if(argc < 2)
207	{
208		LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
209	}
210
211	U32 port = 0;
212	if(!LLStringUtil::convertToU32(argv[1], port))
213	{
214		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
215	}
216
217	// Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown.
218	signal(SIGILL, &crash_handler);		// illegal instruction
219# if LL_DARWIN
220	signal(SIGEMT, &crash_handler);		// emulate instruction executed
221# endif // LL_DARWIN
222	signal(SIGFPE, &crash_handler);		// floating-point exception
223	signal(SIGBUS, &crash_handler);		// bus error
224	signal(SIGSEGV, &crash_handler);	// segmentation violation
225	signal(SIGSYS, &crash_handler);		// non-existent system call invoked
226#endif
227
228#if LL_DARWIN
229	setupCocoa();
230	createAutoReleasePool();
231#endif
232
233	LLPluginProcessChild *plugin = new LLPluginProcessChild();
234
235	plugin->init(port);
236
237#if LL_DARWIN
238		deleteAutoReleasePool();
239#endif
240
241	LLTimer timer;
242	timer.start();
243
244#if LL_WINDOWS
245	checkExceptionHandler();
246#endif
247
248#if LL_DARWIN
249	// If the plugin opens a new window (such as the Flash plugin's fullscreen player), we may need to bring this plugin process to the foreground.
250	// Use this to track the current frontmost window and bring this process to the front if it changes.
251	WindowRef front_window = NULL;
252	WindowGroupRef layer_group = NULL;
253	int window_hack_state = 0;
254	CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group);
255	if(layer_group)
256	{
257		// Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
258		SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
259		SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);		
260	}
261#endif
262
263#if LL_DARWIN
264	EventTargetRef event_target = GetEventDispatcherTarget();
265#endif
266	while(!plugin->isDone())
267	{
268#if LL_DARWIN
269		createAutoReleasePool();
270#endif
271		timer.reset();
272		plugin->idle();
273#if LL_DARWIN
274		{
275			// Some plugins (webkit at least) will want an event loop.  This qualifies.
276			EventRef event;
277			if(ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &event) == noErr)
278			{
279				SendEventToEventTarget (event, event_target);
280				ReleaseEvent(event);
281			}
282			
283			// Check for a change in this process's frontmost window.
284			if(GetFrontWindowOfClass(kAllWindowClasses, true) != front_window)
285			{
286				ProcessSerialNumber self = { 0, kCurrentProcess };
287				ProcessSerialNumber parent = { 0, kNoProcess };
288				ProcessSerialNumber front = { 0, kNoProcess };
289				Boolean this_is_front_process = false;
290				Boolean parent_is_front_process = false;
291				{
292					// Get this process's parent
293					ProcessInfoRec info;
294					info.processInfoLength = sizeof(ProcessInfoRec);
295					info.processName = NULL;
296					info.processAppSpec = NULL;
297					if(GetProcessInformation( &self, &info ) == noErr)
298					{
299						parent = info.processLauncher;
300					}
301					
302					// and figure out whether this process or its parent are currently frontmost
303					if(GetFrontProcess(&front) == noErr)
304					{
305						(void) SameProcess(&self, &front, &this_is_front_process);
306						(void) SameProcess(&parent, &front, &parent_is_front_process);
307					}
308				}
309								
310				if((GetFrontWindowOfClass(kAllWindowClasses, true) != NULL) && (front_window == NULL))
311				{
312					// Opening the first window
313					
314					if(window_hack_state == 0)
315					{
316						// Next time through the event loop, lower the window group layer
317						window_hack_state = 1;
318					}
319
320					if(layer_group)
321					{
322						SetWindowGroup(GetFrontWindowOfClass(kAllWindowClasses, true), layer_group);
323					}
324					
325					if(parent_is_front_process)
326					{
327						// Bring this process's windows to the front.
328						(void) SetFrontProcess( &self );
329					}
330
331					ActivateWindow(GetFrontWindowOfClass(kAllWindowClasses, true), true);
332				}
333				else if((GetFrontWindowOfClass(kAllWindowClasses, true) == NULL) && (front_window != NULL))
334				{
335					// Closing the last window
336					
337					if(this_is_front_process)
338					{
339						// Try to bring this process's parent to the front
340						(void) SetFrontProcess(&parent);
341					}
342				}
343				else if(window_hack_state == 1)
344				{
345					if(layer_group)
346					{
347						// Set the window group level back to something less extreme
348						SetWindowGroupLevel(layer_group, kCGNormalWindowLevel);
349					}
350					window_hack_state = 2;
351				}
352
353				front_window = GetFrontWindowOfClass(kAllWindowClasses, true);
354
355			}
356		}
357#endif
358		F64 elapsed = timer.getElapsedTimeF64();
359		F64 remaining = plugin->getSleepTime() - elapsed;
360
361		if(remaining <= 0.0f)
362		{
363			// We've already used our full allotment.
364//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, not sleeping" << LL_ENDL;
365
366			// Still need to service the network...
367			plugin->pump();
368		}
369		else
370		{
371
372//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
373//			timer.reset();
374
375			// This also services the network as needed.
376			plugin->sleep(remaining);
377
378//			LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" <<  LL_ENDL;
379		}
380
381#if LL_WINDOWS
382	// More agressive checking of interfering exception handlers.
383	// Doesn't appear to be required so far - even for plugins
384	// that do crash with a single call to the intercept
385	// exception handler such as QuickTime.
386	//checkExceptionHandler();
387#endif
388
389#if LL_DARWIN
390		deleteAutoReleasePool();
391#endif
392	}
393
394	delete plugin;
395
396	ll_cleanup_apr();
397
398	return 0;
399}
400