PageRenderTime 190ms CodeModel.GetById 16ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1
  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. #include "linden_common.h"
  31. #include "llpluginprocesschild.h"
  32. #include "llpluginmessage.h"
  33. #include "llerrorcontrol.h"
  34. #include "llapr.h"
  35. #include "llstring.h"
  36. #if LL_DARWIN
  37. #include <Carbon/Carbon.h>
  38. #include "slplugin-objc.h"
  39. #endif
  40. #if LL_DARWIN || LL_LINUX
  41. #include <signal.h>
  42. #endif
  43. /*
  44. 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.
  45. 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:
  46. -sectcreate __TEXT __info_plist /path/to/slplugin_info.plist
  47. which means adding this to the gcc flags:
  48. -Wl,-sectcreate,__TEXT,__info_plist,/path/to/slplugin_info.plist
  49. 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.
  50. */
  51. #if LL_DARWIN || LL_LINUX
  52. // Signal handlers to make crashes not show an OS dialog...
  53. static void crash_handler(int sig)
  54. {
  55. // Just exit cleanly.
  56. // TODO: add our own crash reporting
  57. _exit(1);
  58. }
  59. #endif
  60. #if LL_WINDOWS
  61. #include <windows.h>
  62. ////////////////////////////////////////////////////////////////////////////////
  63. // Our exception handler - will probably just exit and the host application
  64. // will miss the heartbeat and log the error in the usual fashion.
  65. LONG WINAPI myWin32ExceptionHandler( struct _EXCEPTION_POINTERS* exception_infop )
  66. {
  67. //std::cerr << "This plugin (" << __FILE__ << ") - ";
  68. //std::cerr << "intercepted an unhandled exception and will exit immediately." << std::endl;
  69. // TODO: replace exception handler before we exit?
  70. return EXCEPTION_EXECUTE_HANDLER;
  71. }
  72. // Taken from : http://blog.kalmbachnet.de/?postid=75
  73. // The MSVC 2005 CRT forces the call of the default-debugger (normally Dr.Watson)
  74. // even with the other exception handling code. This (terrifying) piece of code
  75. // patches things so that doesn't happen.
  76. LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
  77. LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter )
  78. {
  79. return NULL;
  80. }
  81. BOOL PreventSetUnhandledExceptionFilter()
  82. {
  83. // WARNING: This won't work on 64-bit Windows systems so we turn it off it.
  84. // It should work for any flavor of 32-bit Windows we care about.
  85. // If it's off, sometimes you will see an OS message when a plugin crashes
  86. #ifndef _WIN64
  87. HMODULE hKernel32 = LoadLibraryA( "kernel32.dll" );
  88. if ( NULL == hKernel32 )
  89. return FALSE;
  90. void *pOrgEntry = GetProcAddress( hKernel32, "SetUnhandledExceptionFilter" );
  91. if( NULL == pOrgEntry )
  92. return FALSE;
  93. unsigned char newJump[ 100 ];
  94. DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
  95. dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
  96. void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
  97. DWORD dwNewEntryAddr = (DWORD) pNewFunc;
  98. DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
  99. newJump[ 0 ] = 0xE9; // JMP absolute
  100. memcpy( &newJump[ 1 ], &dwRelativeAddr, sizeof( pNewFunc ) );
  101. SIZE_T bytesWritten;
  102. BOOL bRet = WriteProcessMemory( GetCurrentProcess(), pOrgEntry, newJump, sizeof( pNewFunc ) + 1, &bytesWritten );
  103. return bRet;
  104. #else
  105. return FALSE;
  106. #endif
  107. }
  108. ////////////////////////////////////////////////////////////////////////////////
  109. // Hook our exception handler and replace the system one
  110. void initExceptionHandler()
  111. {
  112. LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  113. // save old exception handler in case we need to restore it at the end
  114. prev_filter = SetUnhandledExceptionFilter( myWin32ExceptionHandler );
  115. PreventSetUnhandledExceptionFilter();
  116. }
  117. bool checkExceptionHandler()
  118. {
  119. bool ok = true;
  120. LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  121. prev_filter = SetUnhandledExceptionFilter(myWin32ExceptionHandler);
  122. PreventSetUnhandledExceptionFilter();
  123. if (prev_filter != myWin32ExceptionHandler)
  124. {
  125. LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with " << prev_filter << "!" << LL_ENDL;
  126. ok = false;
  127. }
  128. if (prev_filter == NULL)
  129. {
  130. ok = FALSE;
  131. if (NULL == myWin32ExceptionHandler)
  132. {
  133. LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
  134. }
  135. else
  136. {
  137. LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with NULL!" << LL_ENDL;
  138. }
  139. }
  140. return ok;
  141. }
  142. #endif
  143. // If this application on Windows platform is a console application, a console is always
  144. // created which is bad. Making it a Windows "application" via CMake settings but not
  145. // adding any code to explicitly create windows does the right thing.
  146. #if LL_WINDOWS
  147. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
  148. #else
  149. int main(int argc, char **argv)
  150. #endif
  151. {
  152. ll_init_apr();
  153. // Set up llerror logging
  154. {
  155. LLError::initForApplication(".");
  156. LLError::setDefaultLevel(LLError::LEVEL_INFO);
  157. // LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG);
  158. // LLError::logToFile("slplugin.log");
  159. }
  160. #if LL_WINDOWS
  161. if( strlen( lpCmdLine ) == 0 )
  162. {
  163. LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
  164. };
  165. U32 port = 0;
  166. if(!LLStringUtil::convertToU32(lpCmdLine, port))
  167. {
  168. LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
  169. };
  170. // Insert our exception handler into the system so this plugin doesn't
  171. // display a crash message if something bad happens. The host app will
  172. // see the missing heartbeat and log appropriately.
  173. initExceptionHandler();
  174. #elif LL_DARWIN || LL_LINUX
  175. if(argc < 2)
  176. {
  177. LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
  178. }
  179. U32 port = 0;
  180. if(!LLStringUtil::convertToU32(argv[1], port))
  181. {
  182. LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
  183. }
  184. // Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown.
  185. signal(SIGILL, &crash_handler); // illegal instruction
  186. # if LL_DARWIN
  187. signal(SIGEMT, &crash_handler); // emulate instruction executed
  188. # endif // LL_DARWIN
  189. signal(SIGFPE, &crash_handler); // floating-point exception
  190. signal(SIGBUS, &crash_handler); // bus error
  191. signal(SIGSEGV, &crash_handler); // segmentation violation
  192. signal(SIGSYS, &crash_handler); // non-existent system call invoked
  193. #endif
  194. #if LL_DARWIN
  195. setupCocoa();
  196. createAutoReleasePool();
  197. #endif
  198. LLPluginProcessChild *plugin = new LLPluginProcessChild();
  199. plugin->init(port);
  200. #if LL_DARWIN
  201. deleteAutoReleasePool();
  202. #endif
  203. LLTimer timer;
  204. timer.start();
  205. #if LL_WINDOWS
  206. checkExceptionHandler();
  207. #endif
  208. #if LL_DARWIN
  209. // 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.
  210. // Use this to track the current frontmost window and bring this process to the front if it changes.
  211. WindowRef front_window = NULL;
  212. WindowGroupRef layer_group = NULL;
  213. int window_hack_state = 0;
  214. CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group);
  215. if(layer_group)
  216. {
  217. // 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)
  218. SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
  219. SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
  220. }
  221. #endif
  222. #if LL_DARWIN
  223. EventTargetRef event_target = GetEventDispatcherTarget();
  224. #endif
  225. while(!plugin->isDone())
  226. {
  227. #if LL_DARWIN
  228. createAutoReleasePool();
  229. #endif
  230. timer.reset();
  231. plugin->idle();
  232. #if LL_DARWIN
  233. {
  234. // Some plugins (webkit at least) will want an event loop. This qualifies.
  235. EventRef event;
  236. if(ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &event) == noErr)
  237. {
  238. SendEventToEventTarget (event, event_target);
  239. ReleaseEvent(event);
  240. }
  241. // Check for a change in this process's frontmost window.
  242. if(GetFrontWindowOfClass(kAllWindowClasses, true) != front_window)
  243. {
  244. ProcessSerialNumber self = { 0, kCurrentProcess };
  245. ProcessSerialNumber parent = { 0, kNoProcess };
  246. ProcessSerialNumber front = { 0, kNoProcess };
  247. Boolean this_is_front_process = false;
  248. Boolean parent_is_front_process = false;
  249. {
  250. // Get this process's parent
  251. ProcessInfoRec info;
  252. info.processInfoLength = sizeof(ProcessInfoRec);
  253. info.processName = NULL;
  254. info.processAppSpec = NULL;
  255. if(GetProcessInformation( &self, &info ) == noErr)
  256. {
  257. parent = info.processLauncher;
  258. }
  259. // and figure out whether this process or its parent are currently frontmost
  260. if(GetFrontProcess(&front) == noErr)
  261. {
  262. (void) SameProcess(&self, &front, &this_is_front_process);
  263. (void) SameProcess(&parent, &front, &parent_is_front_process);
  264. }
  265. }
  266. if((GetFrontWindowOfClass(kAllWindowClasses, true) != NULL) && (front_window == NULL))
  267. {
  268. // Opening the first window
  269. if(window_hack_state == 0)
  270. {
  271. // Next time through the event loop, lower the window group layer
  272. window_hack_state = 1;
  273. }
  274. if(layer_group)
  275. {
  276. SetWindowGroup(GetFrontWindowOfClass(kAllWindowClasses, true), layer_group);
  277. }
  278. if(parent_is_front_process)
  279. {
  280. // Bring this process's windows to the front.
  281. (void) SetFrontProcess( &self );
  282. }
  283. ActivateWindow(GetFrontWindowOfClass(kAllWindowClasses, true), true);
  284. }
  285. else if((GetFrontWindowOfClass(kAllWindowClasses, true) == NULL) && (front_window != NULL))
  286. {
  287. // Closing the last window
  288. if(this_is_front_process)
  289. {
  290. // Try to bring this process's parent to the front
  291. (void) SetFrontProcess(&parent);
  292. }
  293. }
  294. else if(window_hack_state == 1)
  295. {
  296. if(layer_group)
  297. {
  298. // Set the window group level back to something less extreme
  299. SetWindowGroupLevel(layer_group, kCGNormalWindowLevel);
  300. }
  301. window_hack_state = 2;
  302. }
  303. front_window = GetFrontWindowOfClass(kAllWindowClasses, true);
  304. }
  305. }
  306. #endif
  307. F64 elapsed = timer.getElapsedTimeF64();
  308. F64 remaining = plugin->getSleepTime() - elapsed;
  309. if(remaining <= 0.0f)
  310. {
  311. // We've already used our full allotment.
  312. // LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, not sleeping" << LL_ENDL;
  313. // Still need to service the network...
  314. plugin->pump();
  315. }
  316. else
  317. {
  318. // LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
  319. // timer.reset();
  320. // This also services the network as needed.
  321. plugin->sleep(remaining);
  322. // LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" << LL_ENDL;
  323. }
  324. #if LL_WINDOWS
  325. // More agressive checking of interfering exception handlers.
  326. // Doesn't appear to be required so far - even for plugins
  327. // that do crash with a single call to the intercept
  328. // exception handler such as QuickTime.
  329. //checkExceptionHandler();
  330. #endif
  331. #if LL_DARWIN
  332. deleteAutoReleasePool();
  333. #endif
  334. }
  335. delete plugin;
  336. ll_cleanup_apr();
  337. return 0;
  338. }