Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*************************************************************************
3 : *
4 : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : *
6 : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : *
8 : * OpenOffice.org - a multi-platform office productivity suite
9 : *
10 : * This file is part of OpenOffice.org.
11 : *
12 : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : * it under the terms of the GNU Lesser General Public License version 3
14 : * only, as published by the Free Software Foundation.
15 : *
16 : * OpenOffice.org 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
19 : * GNU Lesser General Public License version 3 for more details
20 : * (a copy is included in the LICENSE file that accompanied this code).
21 : *
22 : * You should have received a copy of the GNU Lesser General Public License
23 : * version 3 along with OpenOffice.org. If not, see
24 : * <http://www.openoffice.org/license.html>
25 : * for a copy of the LGPLv3 License.
26 : *
27 : ************************************************************************/
28 :
29 : #ifdef AIX
30 : #define _LINUX_SOURCE_COMPAT
31 : #include <sys/timer.h>
32 : #undef _LINUX_SOURCE_COMPAT
33 : #endif
34 : #include <errno.h>
35 : #include <dlfcn.h>
36 : #include <unistd.h>
37 : #include <sys/poll.h>
38 : #include <fcntl.h>
39 : #include <signal.h>
40 :
41 : #include <plugin/unx/plugcon.hxx>
42 :
43 : #include <osl/file.h>
44 : #include <osl/module.h>
45 :
46 : #include <config_vclplug.h>
47 :
48 : PluginConnector* pConnector = NULL;
49 :
50 : int nAppArguments = 0;
51 : char** pAppArguments = NULL;
52 : Display* pAppDisplay = NULL;
53 : Display* pXtAppDisplay = NULL;
54 :
55 : extern oslModule pPluginLib;
56 : extern NPError (*pNP_Shutdown)();
57 :
58 : void LoadAdditionalLibs(const char*);
59 :
60 : XtAppContext app_context;
61 : Widget topLevel = NULL, topBox = NULL;
62 : int wakeup_fd[2] = { 0, 0 };
63 : static bool bPluginAppQuit = false;
64 :
65 0 : static long GlobalConnectionLostHdl( void* /*pInst*/, void* /*pArg*/ )
66 : {
67 : SAL_WARN("extensions.plugin", "pluginapp exiting due to connection lost");
68 :
69 0 : bool bSuccess = (4 == write(wakeup_fd[1], "xxxx", 4 ));
70 : SAL_WARN_IF(!bSuccess, "extensions.plugin", "short write");
71 0 : return 0;
72 : }
73 :
74 : extern "C"
75 : {
76 0 : static int plugin_x_error_handler( Display*, XErrorEvent* )
77 : {
78 0 : return 0;
79 : }
80 :
81 : #if ! ENABLE_GTK
82 : static void ThreadEventHandler( XtPointer /*client_data*/, int* /*source*/, XtInputId* id )
83 : {
84 : char buf[256];
85 : // clear pipe
86 : int len, nLast = -1;
87 :
88 : while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 )
89 : nLast = len-1;
90 : if( ! bPluginAppQuit )
91 : {
92 : if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector )
93 : pConnector->CallWorkHandler();
94 : else
95 : {
96 : // it seems you can use XtRemoveInput only
97 : // safely from within the callback
98 : // why is that ?
99 : SAL_INFO("extensions.plugin", "removing wakeup pipe");
100 : XtRemoveInput( *id );
101 : XtAppSetExitFlag( app_context );
102 : bPluginAppQuit = true;
103 :
104 : delete pConnector;
105 : pConnector = NULL;
106 : }
107 : }
108 : }
109 : #endif
110 : }
111 :
112 :
113 0 : IMPL_LINK( PluginConnector, NewMessageHdl, Mediator*, /*pMediator*/ )
114 : {
115 : SAL_INFO("extensions.plugin", "new message handler");
116 0 : bool bSuccess = (4 == write(wakeup_fd[1], "cccc", 4));
117 : SAL_WARN_IF(!bSuccess, "extensions.plugin", "short write");
118 0 : return 0;
119 :
120 : }
121 :
122 0 : Widget createSubWidget( char* /*pPluginText*/, Widget shell, XLIB_Window aParentWindow )
123 : {
124 : Widget newWidget = XtVaCreateManagedWidget(
125 : #if defined USE_MOTIF
126 : "drawingArea",
127 : xmDrawingAreaWidgetClass,
128 : #else
129 : "",
130 : compositeWidgetClass,
131 : #endif
132 : shell,
133 : XtNwidth, 200,
134 : XtNheight, 200,
135 0 : (char *)NULL );
136 0 : XtRealizeWidget( shell );
137 0 : XtRealizeWidget( newWidget );
138 :
139 : SAL_INFO(
140 : "extensions.plugin",
141 : "reparenting new widget " << XtWindow( newWidget ) << " to "
142 : << aParentWindow);
143 : XReparentWindow( pXtAppDisplay,
144 : XtWindow( shell ),
145 : aParentWindow,
146 0 : 0, 0 );
147 0 : XtMapWidget( shell );
148 0 : XtMapWidget( newWidget );
149 0 : XRaiseWindow( pXtAppDisplay, XtWindow( shell ) );
150 0 : XSync( pXtAppDisplay, False );
151 :
152 0 : return newWidget;
153 : }
154 :
155 0 : void* CreateNewShell( void** pShellReturn, XLIB_Window aParentWindow )
156 : {
157 : XLIB_String n, c;
158 0 : XtGetApplicationNameAndClass(pXtAppDisplay, &n, &c);
159 :
160 : Widget newShell =
161 : XtVaAppCreateShell( "pane", c,
162 : topLevelShellWidgetClass,
163 : pXtAppDisplay,
164 : XtNwidth, 200,
165 : XtNheight, 200,
166 : XtNoverrideRedirect, True,
167 0 : (char *)NULL );
168 0 : *pShellReturn = newShell;
169 :
170 : char pText[1024];
171 0 : sprintf( pText, "starting plugin %s ...", pAppArguments[2] );
172 :
173 0 : Widget newWidget = createSubWidget( pText, newShell, aParentWindow );
174 :
175 0 : return newWidget;
176 : }
177 :
178 0 : static oslModule LoadModule( const char* pPath )
179 : {
180 0 : OUString sSystemPath( OUString::createFromAscii( pPath ) );
181 0 : OUString sFileURL;
182 0 : osl_getFileURLFromSystemPath( sSystemPath.pData, &sFileURL.pData );
183 :
184 0 : oslModule pLib = osl_loadModule( sFileURL.pData, SAL_LOADMODULE_LAZY );
185 : SAL_INFO_IF(!pLib, "extensions.plugin", "could not open " << pPath);
186 0 : return pLib;
187 : }
188 :
189 : // Unix specific implementation
190 0 : static void CheckPlugin( const char* pPath )
191 : {
192 0 : oslModule pLib = LoadModule( pPath );
193 0 : if (pLib != 0)
194 : {
195 : char*(*pNP_GetMIMEDescription)() = (char*(*)())
196 0 : osl_getAsciiFunctionSymbol( pLib, "NP_GetMIMEDescription" );
197 0 : if( pNP_GetMIMEDescription )
198 0 : printf( "%s\n", pNP_GetMIMEDescription() );
199 : else
200 : SAL_WARN(
201 : "extensions.plugin",
202 : "could not get symbol NP_GetMIMEDescription " << dlerror());
203 0 : osl_unloadModule( pLib );
204 : }
205 0 : }
206 :
207 : #if OSL_DEBUG_LEVEL > 1 && defined LINUX
208 : #include <execinfo.h>
209 : #endif
210 :
211 : extern "C" {
212 :
213 0 : static void signal_handler( int nSig )
214 : {
215 : #if OSL_DEBUG_LEVEL > 1
216 : fprintf( stderr, "caught signal %d, exiting\n", nSig );
217 : #ifdef LINUX
218 : void* pStack[64];
219 : int nStackLevels = backtrace( pStack, SAL_N_ELEMENTS(pStack) );
220 : backtrace_symbols_fd( pStack, nStackLevels, STDERR_FILENO );
221 : #endif
222 : #endif
223 0 : if( pConnector )
224 : {
225 : // ensure that a read on the other side will wakeup
226 0 : delete pConnector;
227 0 : pConnector = NULL;
228 : }
229 :
230 0 : _exit(nSig);
231 : }
232 :
233 : #if ENABLE_GTK
234 :
235 0 : static gboolean noClosure( gpointer )
236 : {
237 0 : return sal_True;
238 : }
239 :
240 : // Xt events
241 0 : static gboolean prepareXtEvent( GSource*, gint* )
242 : {
243 0 : int nMask = XtAppPending( app_context );
244 0 : return (nMask & XtIMAll) != 0;
245 : }
246 :
247 0 : static gboolean checkXtEvent( GSource* )
248 : {
249 0 : int nMask = XtAppPending( app_context );
250 0 : return (nMask & XtIMAll) != 0;
251 : }
252 :
253 0 : static gboolean dispatchXtEvent( GSource*, GSourceFunc, gpointer )
254 : {
255 0 : XtAppProcessEvent( app_context, XtIMAll );
256 0 : return sal_True;
257 : }
258 :
259 : static GSourceFuncs aXtEventFuncs =
260 : {
261 : prepareXtEvent,
262 : checkXtEvent,
263 : dispatchXtEvent,
264 : NULL,
265 : noClosure,
266 : NULL
267 : };
268 :
269 0 : static gboolean pollXtTimerCallback(gpointer)
270 : {
271 0 : for(int i = 0; i < 5; i++)
272 : {
273 0 : if( (XtAppPending(app_context) & (XtIMAll & ~XtIMXEvent)) == 0 )
274 0 : break;
275 0 : XtAppProcessEvent(app_context, XtIMAll & ~XtIMXEvent);
276 : }
277 0 : return sal_True;
278 : }
279 :
280 0 : static gboolean prepareWakeupEvent( GSource*, gint* )
281 : {
282 0 : struct pollfd aPoll = { wakeup_fd[0], POLLIN, 0 };
283 0 : poll( &aPoll, 1, 0 );
284 0 : return (aPoll.revents & POLLIN ) != 0;
285 : }
286 :
287 0 : static gboolean checkWakeupEvent( GSource* pSource )
288 : {
289 0 : gint nDum = 0;
290 0 : return prepareWakeupEvent( pSource, &nDum );
291 : }
292 :
293 0 : static gboolean dispatchWakeupEvent( GSource*, GSourceFunc, gpointer )
294 : {
295 : char buf[256];
296 : // clear pipe
297 0 : int len, nLast = -1;
298 :
299 0 : while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 )
300 0 : nLast = len-1;
301 0 : if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector )
302 0 : pConnector->CallWorkHandler();
303 : else
304 : {
305 0 : XtAppSetExitFlag( app_context );
306 0 : bPluginAppQuit = true;
307 :
308 0 : delete pConnector;
309 0 : pConnector = NULL;
310 : }
311 :
312 0 : return sal_True;
313 : }
314 :
315 : static GSourceFuncs aWakeupEventFuncs = {
316 : prepareWakeupEvent,
317 : checkWakeupEvent,
318 : dispatchWakeupEvent,
319 : NULL,
320 : noClosure,
321 : NULL
322 : };
323 :
324 : #endif // GTK
325 :
326 : }
327 :
328 0 : int main( int argc, char **argv)
329 : {
330 : struct sigaction aSigAction;
331 0 : aSigAction.sa_handler = signal_handler;
332 0 : sigemptyset( &aSigAction.sa_mask );
333 0 : aSigAction.sa_flags = SA_NOCLDSTOP;
334 0 : sigaction( SIGSEGV, &aSigAction, NULL );
335 0 : sigaction( SIGBUS, &aSigAction, NULL );
336 0 : sigaction( SIGABRT, &aSigAction, NULL );
337 0 : sigaction( SIGTERM, &aSigAction, NULL );
338 0 : sigaction( SIGILL, &aSigAction, NULL );
339 :
340 0 : int nArg = (argc < 3) ? 1 : 2;
341 0 : char* pBaseName = argv[nArg] + strlen(argv[nArg]);
342 0 : while( pBaseName > argv[nArg] && pBaseName[-1] != '/' )
343 0 : pBaseName--;
344 0 : LoadAdditionalLibs( pBaseName );
345 :
346 0 : if( argc == 2 )
347 : {
348 0 : CheckPlugin(argv[1]);
349 0 : exit(0);
350 : }
351 0 : nAppArguments = argc;
352 0 : pAppArguments = argv;
353 :
354 0 : XSetErrorHandler( plugin_x_error_handler );
355 :
356 0 : if( pipe( wakeup_fd ) )
357 : {
358 : SAL_WARN("extensions.plugin", "could not pipe()");
359 0 : return 1;
360 : }
361 : // initialize 'wakeup' pipe.
362 : int flags;
363 :
364 : // set close-on-exec descriptor flag.
365 0 : if ((flags = fcntl (wakeup_fd[0], F_GETFD)) != -1)
366 : {
367 0 : flags |= FD_CLOEXEC;
368 0 : fcntl (wakeup_fd[0], F_SETFD, flags);
369 : }
370 0 : if ((flags = fcntl (wakeup_fd[1], F_GETFD)) != -1)
371 : {
372 0 : flags |= FD_CLOEXEC;
373 0 : fcntl (wakeup_fd[1], F_SETFD, flags);
374 : }
375 :
376 : // set non-blocking I/O flag.
377 0 : if ((flags = fcntl (wakeup_fd[0], F_GETFL)) != -1)
378 : {
379 0 : flags |= O_NONBLOCK;
380 0 : fcntl (wakeup_fd[0], F_SETFL, flags);
381 : }
382 0 : if ((flags = fcntl (wakeup_fd[1], F_GETFL)) != -1)
383 : {
384 0 : flags |= O_NONBLOCK;
385 0 : fcntl (wakeup_fd[1], F_SETFL, flags);
386 : }
387 :
388 0 : pPluginLib = LoadModule( argv[2] );
389 0 : if( ! pPluginLib )
390 : {
391 0 : exit(255);
392 : }
393 0 : int nSocket = atol( argv[1] );
394 :
395 : #if ENABLE_GTK
396 0 : g_thread_init(NULL);
397 0 : gtk_init(&argc, &argv);
398 : #endif
399 :
400 0 : pConnector = new PluginConnector( nSocket );
401 0 : pConnector->SetConnectionLostHdl( Link( NULL, GlobalConnectionLostHdl ) );
402 :
403 0 : XtSetLanguageProc( NULL, NULL, NULL );
404 :
405 0 : XtToolkitInitialize();
406 0 : app_context = XtCreateApplicationContext();
407 0 : pXtAppDisplay = XtOpenDisplay( app_context, NULL, "SOPlugin", "SOPlugin", NULL, 0, &argc, argv );
408 :
409 :
410 : #if ENABLE_GTK
411 : // integrate Xt events into GTK event loop
412 : GPollFD aXtPollDesc, aWakeupPollDesc;
413 :
414 0 : GSource* pXTSource = g_source_new( &aXtEventFuncs, sizeof(GSource) );
415 0 : if( !pXTSource )
416 : {
417 : SAL_WARN("extensions.plugin", "could not get Xt GSource");
418 0 : return 1;
419 : }
420 :
421 0 : g_source_set_priority( pXTSource, GDK_PRIORITY_EVENTS );
422 0 : g_source_set_can_recurse( pXTSource, sal_True );
423 0 : g_source_attach( pXTSource, NULL );
424 0 : aXtPollDesc.fd = ConnectionNumber( pXtAppDisplay );
425 0 : aXtPollDesc.events = G_IO_IN;
426 0 : aXtPollDesc.revents = 0;
427 0 : g_source_add_poll( pXTSource, &aXtPollDesc );
428 :
429 0 : gint xt_polling_timer_id = g_timeout_add( 25, pollXtTimerCallback, NULL);
430 : // Initialize wakeup events listener
431 0 : GSource *pWakeupSource = g_source_new( &aWakeupEventFuncs, sizeof(GSource) );
432 0 : if ( pWakeupSource == NULL )
433 : {
434 : SAL_WARN("extensions.plugin", "could not get wakeup source");
435 0 : return 1;
436 : }
437 0 : g_source_set_priority( pWakeupSource, GDK_PRIORITY_EVENTS);
438 0 : g_source_attach( pWakeupSource, NULL );
439 0 : aWakeupPollDesc.fd = wakeup_fd[0];
440 0 : aWakeupPollDesc.events = G_IO_IN;
441 0 : aWakeupPollDesc.revents = 0;
442 0 : g_source_add_poll( pWakeupSource, &aWakeupPollDesc );
443 :
444 0 : pAppDisplay = gdk_x11_display_get_xdisplay( gdk_display_get_default() );
445 : #else
446 : pAppDisplay = pXtAppDisplay;
447 : XtAppAddInput( app_context,
448 : wakeup_fd[0],
449 : (XtPointer)XtInputReadMask,
450 : ThreadEventHandler, NULL );
451 : #endif
452 :
453 : // send that we are ready to go
454 : MediatorMessage* pMessage =
455 : pConnector->Transact( "init req", 8,
456 0 : NULL );
457 0 : delete pMessage;
458 :
459 : #if OSL_DEBUG_LEVEL > 3
460 : int nPID = getpid();
461 : int nChild = fork();
462 : if( nChild == 0 )
463 : {
464 : char pidbuf[16];
465 : char* pArgs[] = { "xterm", "-sl", "2000", "-sb", "-e", "gdb", "pluginapp.bin", pidbuf, NULL };
466 : sprintf( pidbuf, "%d", nPID );
467 : execvp( pArgs[0], pArgs );
468 : _exit(255);
469 : }
470 : else
471 : sleep( 10 );
472 : #endif
473 :
474 : /*
475 : * Loop for events.
476 : */
477 : // for some reason XtAppSetExitFlag won't quit the application
478 : // in ThreadEventHandler most of times; Xt will hang in select
479 : // (hat is in XtAppNextEvent). Have our own mainloop instead
480 : // of XtAppMainLoop
481 0 : do
482 : {
483 : #if ENABLE_GTK
484 0 : g_main_context_iteration( NULL, sal_True );
485 : #else
486 : XtAppProcessEvent( app_context, XtIMAll );
487 : #endif
488 0 : } while( ! XtAppGetExitFlag( app_context ) && ! bPluginAppQuit );
489 :
490 : SAL_INFO("extensions.plugin", "left plugin app main loop");
491 :
492 : #if ENABLE_GTK
493 0 : g_source_remove(xt_polling_timer_id);
494 : #endif
495 :
496 0 : pNP_Shutdown();
497 : SAL_INFO("extensions.plugin", "NP_Shutdown done");
498 0 : osl_unloadModule( pPluginLib );
499 : SAL_INFO("extensions.plugin", "plugin close");
500 :
501 0 : close( wakeup_fd[0] );
502 0 : close( wakeup_fd[1] );
503 :
504 0 : return 0;
505 : }
506 :
507 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|