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